xref: /libuv/test/test-async.c (revision 011a1ac1)
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 #include <stdio.h>
25 #include <stdlib.h>
26 
27 static uv_thread_t thread;
28 static uv_mutex_t mutex;
29 
30 static uv_prepare_t prepare;
31 static uv_async_t async;
32 
33 static volatile int async_cb_called;
34 static int prepare_cb_called;
35 static int close_cb_called;
36 
37 
thread_cb(void * arg)38 static void thread_cb(void *arg) {
39   int n;
40   int r;
41 
42   for (;;) {
43     uv_mutex_lock(&mutex);
44     n = async_cb_called;
45     uv_mutex_unlock(&mutex);
46 
47     if (n == 3) {
48       break;
49     }
50 
51     r = uv_async_send(&async);
52     ASSERT_OK(r);
53 
54     /* Work around a bug in Valgrind.
55      *
56      * Valgrind runs threads not in parallel but sequentially, i.e. one after
57      * the other. It also doesn't preempt them, instead it depends on threads
58      * yielding voluntarily by making a syscall.
59      *
60      * That never happens here: the pipe that is associated with the async
61      * handle is written to once but that's too early for Valgrind's scheduler
62      * to kick in. Afterwards, the thread busy-loops, starving the main thread.
63      * Therefore, we yield.
64      *
65      * This behavior has been observed with Valgrind 3.7.0 and 3.9.0.
66      */
67     uv_sleep(0);
68   }
69 }
70 
71 
close_cb(uv_handle_t * handle)72 static void close_cb(uv_handle_t* handle) {
73   ASSERT_NOT_NULL(handle);
74   close_cb_called++;
75 }
76 
77 
async_cb(uv_async_t * handle)78 static void async_cb(uv_async_t* handle) {
79   int n;
80 
81   ASSERT_PTR_EQ(handle, &async);
82 
83   uv_mutex_lock(&mutex);
84   n = ++async_cb_called;
85   uv_mutex_unlock(&mutex);
86 
87   if (n == 3) {
88     uv_close((uv_handle_t*)&async, close_cb);
89     uv_close((uv_handle_t*)&prepare, close_cb);
90   }
91 }
92 
93 
prepare_cb(uv_prepare_t * handle)94 static void prepare_cb(uv_prepare_t* handle) {
95   int r;
96 
97   ASSERT_PTR_EQ(handle, &prepare);
98 
99   if (prepare_cb_called++)
100     return;
101 
102   r = uv_thread_create(&thread, thread_cb, NULL);
103   ASSERT_OK(r);
104   uv_mutex_unlock(&mutex);
105 }
106 
107 
TEST_IMPL(async)108 TEST_IMPL(async) {
109   int r;
110 
111   r = uv_mutex_init(&mutex);
112   ASSERT_OK(r);
113   uv_mutex_lock(&mutex);
114 
115   r = uv_prepare_init(uv_default_loop(), &prepare);
116   ASSERT_OK(r);
117   r = uv_prepare_start(&prepare, prepare_cb);
118   ASSERT_OK(r);
119 
120   r = uv_async_init(uv_default_loop(), &async, async_cb);
121   ASSERT_OK(r);
122 
123   r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
124   ASSERT_OK(r);
125 
126   ASSERT_GT(prepare_cb_called, 0);
127   ASSERT_EQ(3, async_cb_called);
128   ASSERT_EQ(2, close_cb_called);
129 
130   ASSERT_OK(uv_thread_join(&thread));
131 
132   MAKE_VALGRIND_HAPPY(uv_default_loop());
133   return 0;
134 }
135