|
160
|
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
|
|
|
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_OK(r);
|
|
|
75 }
|
|
|
76
|
|
|
77
|
|
|
78 static void getaddrinfo_cb(uv_getaddrinfo_t* handle,
|
|
|
79 int status,
|
|
|
80 struct addrinfo* res) {
|
|
|
81 struct getaddrinfo_req* req;
|
|
|
82
|
|
|
83 ASSERT_OK(status);
|
|
|
84
|
|
|
85 req = container_of(handle, struct getaddrinfo_req, handle);
|
|
|
86 uv_freeaddrinfo(res);
|
|
|
87
|
|
|
88 if (--req->counter)
|
|
|
89 getaddrinfo_do(req);
|
|
|
90 }
|
|
|
91
|
|
|
92
|
|
|
93 static void fs_do(struct fs_req* req) {
|
|
|
94 int r;
|
|
|
95
|
|
|
96 r = uv_fs_stat(req->loop, &req->handle, ".", fs_cb);
|
|
|
97 ASSERT_OK(r);
|
|
|
98 }
|
|
|
99
|
|
|
100
|
|
|
101 static void fs_cb(uv_fs_t* handle) {
|
|
|
102 struct fs_req* req = container_of(handle, struct fs_req, handle);
|
|
|
103
|
|
|
104 uv_fs_req_cleanup(handle);
|
|
|
105
|
|
|
106 if (--req->counter)
|
|
|
107 fs_do(req);
|
|
|
108 }
|
|
|
109
|
|
|
110
|
|
|
111 static void do_work(void* arg) {
|
|
|
112 struct getaddrinfo_req getaddrinfo_reqs[4];
|
|
|
113 struct fs_req fs_reqs[4];
|
|
|
114 uv_loop_t loop;
|
|
|
115 size_t i;
|
|
|
116 struct test_thread* thread = arg;
|
|
|
117
|
|
|
118 ASSERT_OK(uv_loop_init(&loop));
|
|
|
119
|
|
|
120 for (i = 0; i < ARRAY_SIZE(getaddrinfo_reqs); i++) {
|
|
|
121 struct getaddrinfo_req* req = getaddrinfo_reqs + i;
|
|
|
122 req->counter = 4;
|
|
|
123 req->loop = &loop;
|
|
|
124 getaddrinfo_do(req);
|
|
|
125 }
|
|
|
126
|
|
|
127 for (i = 0; i < ARRAY_SIZE(fs_reqs); i++) {
|
|
|
128 struct fs_req* req = fs_reqs + i;
|
|
|
129 req->counter = 4;
|
|
|
130 req->loop = &loop;
|
|
|
131 fs_do(req);
|
|
|
132 }
|
|
|
133
|
|
|
134 ASSERT_OK(uv_run(&loop, UV_RUN_DEFAULT));
|
|
|
135 ASSERT_OK(uv_loop_close(&loop));
|
|
|
136 thread->thread_called = 1;
|
|
|
137 }
|
|
|
138
|
|
|
139
|
|
|
140 static void thread_entry(void* arg) {
|
|
|
141 ASSERT_PTR_EQ(arg, (void *) 42);
|
|
|
142 thread_called++;
|
|
|
143 }
|
|
|
144
|
|
|
145
|
|
|
146 TEST_IMPL(thread_create) {
|
|
|
147 uv_thread_t tid;
|
|
|
148 int r;
|
|
|
149
|
|
|
150 r = uv_thread_create(&tid, thread_entry, (void *) 42);
|
|
|
151 ASSERT_OK(r);
|
|
|
152
|
|
|
153 r = uv_thread_join(&tid);
|
|
|
154 ASSERT_OK(r);
|
|
|
155
|
|
|
156 ASSERT_EQ(1, thread_called);
|
|
|
157
|
|
|
158 return 0;
|
|
|
159 }
|
|
|
160
|
|
|
161
|
|
|
162 /* Hilariously bad test name. Run a lot of tasks in the thread pool and verify
|
|
|
163 * that each "finished" callback is run in its originating thread.
|
|
|
164 */
|
|
|
165 TEST_IMPL(threadpool_multiple_event_loops) {
|
|
|
166 /* TODO(gengjiawen): Fix test on QEMU. */
|
|
|
167 #if defined(__QEMU__)
|
|
|
168 RETURN_SKIP("Test does not currently work in QEMU");
|
|
|
169 #endif
|
|
|
170
|
|
|
171 struct test_thread threads[8];
|
|
|
172 size_t i;
|
|
|
173 int r;
|
|
|
174
|
|
|
175 memset(threads, 0, sizeof(threads));
|
|
|
176
|
|
|
177 for (i = 0; i < ARRAY_SIZE(threads); i++) {
|
|
|
178 r = uv_thread_create(&threads[i].thread_id, do_work, &threads[i]);
|
|
|
179 ASSERT_OK(r);
|
|
|
180 }
|
|
|
181
|
|
|
182 for (i = 0; i < ARRAY_SIZE(threads); i++) {
|
|
|
183 r = uv_thread_join(&threads[i].thread_id);
|
|
|
184 ASSERT_OK(r);
|
|
|
185 ASSERT_EQ(1, threads[i].thread_called);
|
|
|
186 }
|
|
|
187
|
|
|
188 return 0;
|
|
|
189 }
|
|
|
190
|
|
|
191
|
|
|
192 static void tls_thread(void* arg) {
|
|
|
193 ASSERT_NULL(uv_key_get(&tls_key));
|
|
|
194 uv_key_set(&tls_key, arg);
|
|
|
195 ASSERT_PTR_EQ(arg, uv_key_get(&tls_key));
|
|
|
196 uv_key_set(&tls_key, NULL);
|
|
|
197 ASSERT_NULL(uv_key_get(&tls_key));
|
|
|
198 }
|
|
|
199
|
|
|
200
|
|
|
201 TEST_IMPL(thread_local_storage) {
|
|
|
202 char name[] = "main";
|
|
|
203 uv_thread_t threads[2];
|
|
|
204 ASSERT_OK(uv_key_create(&tls_key));
|
|
|
205 ASSERT_NULL(uv_key_get(&tls_key));
|
|
|
206 uv_key_set(&tls_key, name);
|
|
|
207 ASSERT_PTR_EQ(name, uv_key_get(&tls_key));
|
|
|
208 ASSERT_OK(uv_thread_create(threads + 0, tls_thread, threads + 0));
|
|
|
209 ASSERT_OK(uv_thread_create(threads + 1, tls_thread, threads + 1));
|
|
|
210 ASSERT_OK(uv_thread_join(threads + 0));
|
|
|
211 ASSERT_OK(uv_thread_join(threads + 1));
|
|
|
212 uv_key_delete(&tls_key);
|
|
|
213 return 0;
|
|
|
214 }
|
|
|
215
|
|
|
216
|
|
|
217 static void thread_check_stack(void* arg) {
|
|
|
218 #if defined(__APPLE__)
|
|
|
219 size_t expected;
|
|
|
220 expected = arg == NULL ? 0 : ((uv_thread_options_t*)arg)->stack_size;
|
|
|
221 /* 512 kB is the default stack size of threads other than the main thread
|
|
|
222 * on MacOS. */
|
|
|
223 if (expected == 0)
|
|
|
224 expected = 512 * 1024;
|
|
|
225 ASSERT_GE(pthread_get_stacksize_np(pthread_self()), expected);
|
|
|
226 #elif defined(__linux__) && defined(__GLIBC__)
|
|
|
227 size_t expected;
|
|
|
228 struct rlimit lim;
|
|
|
229 size_t stack_size;
|
|
|
230 pthread_attr_t attr;
|
|
|
231 ASSERT_OK(getrlimit(RLIMIT_STACK, &lim));
|
|
|
232 if (lim.rlim_cur == RLIM_INFINITY)
|
|
|
233 lim.rlim_cur = 2 << 20; /* glibc default. */
|
|
|
234 ASSERT_OK(pthread_getattr_np(pthread_self(), &attr));
|
|
|
235 ASSERT_OK(pthread_attr_getstacksize(&attr, &stack_size));
|
|
|
236 expected = arg == NULL ? 0 : ((uv_thread_options_t*)arg)->stack_size;
|
|
|
237 if (expected == 0)
|
|
|
238 expected = (size_t)lim.rlim_cur;
|
|
|
239 ASSERT_GE(stack_size, expected);
|
|
|
240 ASSERT_OK(pthread_attr_destroy(&attr));
|
|
|
241 #endif
|
|
|
242 }
|
|
|
243
|
|
|
244
|
|
|
245 TEST_IMPL(thread_stack_size) {
|
|
|
246 uv_thread_t thread;
|
|
|
247 ASSERT_OK(uv_thread_create(&thread, thread_check_stack, NULL));
|
|
|
248 ASSERT_OK(uv_thread_join(&thread));
|
|
|
249 return 0;
|
|
|
250 }
|
|
|
251
|
|
|
252 TEST_IMPL(thread_stack_size_explicit) {
|
|
|
253 uv_thread_t thread;
|
|
|
254 uv_thread_options_t options;
|
|
|
255
|
|
|
256 options.flags = UV_THREAD_HAS_STACK_SIZE;
|
|
|
257 options.stack_size = 1024 * 1024;
|
|
|
258 ASSERT_OK(uv_thread_create_ex(&thread, &options,
|
|
|
259 thread_check_stack, &options));
|
|
|
260 ASSERT_OK(uv_thread_join(&thread));
|
|
|
261
|
|
|
262 options.stack_size = 8 * 1024 * 1024; /* larger than most default os sizes */
|
|
|
263 ASSERT_OK(uv_thread_create_ex(&thread, &options,
|
|
|
264 thread_check_stack, &options));
|
|
|
265 ASSERT_OK(uv_thread_join(&thread));
|
|
|
266
|
|
|
267 options.stack_size = 0;
|
|
|
268 ASSERT_OK(uv_thread_create_ex(&thread, &options,
|
|
|
269 thread_check_stack, &options));
|
|
|
270 ASSERT_OK(uv_thread_join(&thread));
|
|
|
271
|
|
|
272 options.stack_size = 42;
|
|
|
273 ASSERT_OK(uv_thread_create_ex(&thread, &options,
|
|
|
274 thread_check_stack, &options));
|
|
|
275 ASSERT_OK(uv_thread_join(&thread));
|
|
|
276
|
|
|
277 #ifdef PTHREAD_STACK_MIN
|
|
|
278 options.stack_size = PTHREAD_STACK_MIN - 42; /* unaligned size */
|
|
|
279 ASSERT_OK(uv_thread_create_ex(&thread, &options,
|
|
|
280 thread_check_stack, &options));
|
|
|
281 ASSERT_OK(uv_thread_join(&thread));
|
|
|
282
|
|
|
283 options.stack_size = PTHREAD_STACK_MIN / 2 - 42; /* unaligned size */
|
|
|
284 ASSERT_OK(uv_thread_create_ex(&thread, &options,
|
|
|
285 thread_check_stack, &options));
|
|
|
286 ASSERT_OK(uv_thread_join(&thread));
|
|
|
287 #endif
|
|
|
288
|
|
|
289 /* unaligned size, should be larger than PTHREAD_STACK_MIN */
|
|
|
290 options.stack_size = 1234567;
|
|
|
291 ASSERT_OK(uv_thread_create_ex(&thread, &options,
|
|
|
292 thread_check_stack, &options));
|
|
|
293 ASSERT_OK(uv_thread_join(&thread));
|
|
|
294
|
|
|
295 return 0;
|
|
|
296 }
|
|
|
297
|
|
|
298 static void thread_detach_cb(void* arg) {}
|
|
|
299
|
|
|
300 TEST_IMPL(thread_detach) {
|
|
|
301 uv_thread_t thread;
|
|
|
302 ASSERT_OK(uv_thread_create(&thread, thread_detach_cb, NULL));
|
|
|
303 ASSERT_OK(uv_thread_detach(&thread));
|
|
|
304
|
|
|
305 return 0;
|
|
|
306 }
|