xref: /netbsd-src/external/mit/libuv/dist/test/test-thread.c (revision 5f2f42719cd62ff11fd913b40b7ce19f07c4fd25)
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