1 1.1 christos /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 1.1 christos * 3 1.1 christos * Permission is hereby granted, free of charge, to any person obtaining a copy 4 1.1 christos * of this software and associated documentation files (the "Software"), to 5 1.1 christos * deal in the Software without restriction, including without limitation the 6 1.1 christos * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 1.1 christos * sell copies of the Software, and to permit persons to whom the Software is 8 1.1 christos * furnished to do so, subject to the following conditions: 9 1.1 christos * 10 1.1 christos * The above copyright notice and this permission notice shall be included in 11 1.1 christos * all copies or substantial portions of the Software. 12 1.1 christos * 13 1.1 christos * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 1.1 christos * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 1.1 christos * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 1.1 christos * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 1.1 christos * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 1.1 christos * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 1.1 christos * IN THE SOFTWARE. 20 1.1 christos */ 21 1.1 christos 22 1.1 christos #include "uv.h" 23 1.1 christos #include "task.h" 24 1.1 christos 25 1.1.1.3 christos #ifdef _WIN32 26 1.1.1.3 christos # define putenv _putenv 27 1.1.1.3 christos #endif 28 1.1.1.3 christos 29 1.1 christos #define INIT_CANCEL_INFO(ci, what) \ 30 1.1 christos do { \ 31 1.1 christos (ci)->reqs = (what); \ 32 1.1 christos (ci)->nreqs = ARRAY_SIZE(what); \ 33 1.1 christos (ci)->stride = sizeof((what)[0]); \ 34 1.1 christos } \ 35 1.1 christos while (0) 36 1.1 christos 37 1.1 christos struct cancel_info { 38 1.1 christos void* reqs; 39 1.1 christos unsigned nreqs; 40 1.1 christos unsigned stride; 41 1.1 christos uv_timer_t timer_handle; 42 1.1 christos }; 43 1.1 christos 44 1.1 christos struct random_info { 45 1.1 christos uv_random_t random_req; 46 1.1 christos char buf[1]; 47 1.1 christos }; 48 1.1 christos 49 1.1 christos static unsigned fs_cb_called; 50 1.1 christos static unsigned done_cb_called; 51 1.1 christos static unsigned done2_cb_called; 52 1.1 christos static unsigned timer_cb_called; 53 1.1 christos static uv_work_t pause_reqs[4]; 54 1.1 christos static uv_sem_t pause_sems[ARRAY_SIZE(pause_reqs)]; 55 1.1 christos 56 1.1 christos 57 1.1 christos static void work_cb(uv_work_t* req) { 58 1.1 christos uv_sem_wait(pause_sems + (req - pause_reqs)); 59 1.1 christos } 60 1.1 christos 61 1.1 christos 62 1.1 christos static void done_cb(uv_work_t* req, int status) { 63 1.1 christos uv_sem_destroy(pause_sems + (req - pause_reqs)); 64 1.1 christos } 65 1.1 christos 66 1.1 christos 67 1.1 christos static void saturate_threadpool(void) { 68 1.1 christos uv_loop_t* loop; 69 1.1 christos char buf[64]; 70 1.1 christos size_t i; 71 1.1 christos 72 1.1 christos snprintf(buf, 73 1.1 christos sizeof(buf), 74 1.1 christos "UV_THREADPOOL_SIZE=%lu", 75 1.1 christos (unsigned long)ARRAY_SIZE(pause_reqs)); 76 1.1 christos putenv(buf); 77 1.1 christos 78 1.1 christos loop = uv_default_loop(); 79 1.1 christos for (i = 0; i < ARRAY_SIZE(pause_reqs); i += 1) { 80 1.1.1.3 christos ASSERT_OK(uv_sem_init(pause_sems + i, 0)); 81 1.1.1.3 christos ASSERT_OK(uv_queue_work(loop, pause_reqs + i, work_cb, done_cb)); 82 1.1 christos } 83 1.1 christos } 84 1.1 christos 85 1.1 christos 86 1.1 christos static void unblock_threadpool(void) { 87 1.1 christos size_t i; 88 1.1 christos 89 1.1 christos for (i = 0; i < ARRAY_SIZE(pause_reqs); i += 1) 90 1.1 christos uv_sem_post(pause_sems + i); 91 1.1 christos } 92 1.1 christos 93 1.1 christos 94 1.1.1.3 christos static int known_broken(uv_req_t* req) { 95 1.1.1.3 christos if (req->type != UV_FS) 96 1.1.1.3 christos return 0; 97 1.1.1.3 christos 98 1.1.1.3 christos #ifdef __linux__ 99 1.1.1.3 christos /* TODO(bnoordhuis) make cancellation work with io_uring */ 100 1.1.1.3 christos switch (((uv_fs_t*) req)->fs_type) { 101 1.1.1.3 christos case UV_FS_CLOSE: 102 1.1.1.3 christos case UV_FS_FDATASYNC: 103 1.1.1.3 christos case UV_FS_FSTAT: 104 1.1.1.3 christos case UV_FS_FSYNC: 105 1.1.1.3 christos case UV_FS_LINK: 106 1.1.1.3 christos case UV_FS_LSTAT: 107 1.1.1.3 christos case UV_FS_MKDIR: 108 1.1.1.3 christos case UV_FS_OPEN: 109 1.1.1.3 christos case UV_FS_READ: 110 1.1.1.3 christos case UV_FS_RENAME: 111 1.1.1.3 christos case UV_FS_STAT: 112 1.1.1.3 christos case UV_FS_SYMLINK: 113 1.1.1.3 christos case UV_FS_WRITE: 114 1.1.1.3 christos case UV_FS_UNLINK: 115 1.1.1.3 christos return 1; 116 1.1.1.3 christos default: /* Squelch -Wswitch warnings. */ 117 1.1.1.3 christos break; 118 1.1.1.3 christos } 119 1.1.1.3 christos #endif 120 1.1.1.3 christos 121 1.1.1.3 christos return 0; 122 1.1.1.3 christos } 123 1.1.1.3 christos 124 1.1.1.3 christos 125 1.1 christos static void fs_cb(uv_fs_t* req) { 126 1.1.1.3 christos ASSERT_NE(known_broken((uv_req_t*) req) || \ 127 1.1.1.3 christos req->result == UV_ECANCELED, 0); 128 1.1 christos uv_fs_req_cleanup(req); 129 1.1 christos fs_cb_called++; 130 1.1 christos } 131 1.1 christos 132 1.1 christos 133 1.1 christos static void getaddrinfo_cb(uv_getaddrinfo_t* req, 134 1.1 christos int status, 135 1.1 christos struct addrinfo* res) { 136 1.1.1.3 christos ASSERT_EQ(status, UV_EAI_CANCELED); 137 1.1.1.2 christos ASSERT_NULL(res); 138 1.1 christos uv_freeaddrinfo(res); /* Should not crash. */ 139 1.1 christos } 140 1.1 christos 141 1.1 christos 142 1.1 christos static void getnameinfo_cb(uv_getnameinfo_t* handle, 143 1.1 christos int status, 144 1.1 christos const char* hostname, 145 1.1 christos const char* service) { 146 1.1.1.3 christos ASSERT_EQ(status, UV_EAI_CANCELED); 147 1.1.1.2 christos ASSERT_NULL(hostname); 148 1.1.1.2 christos ASSERT_NULL(service); 149 1.1 christos } 150 1.1 christos 151 1.1 christos 152 1.1 christos static void work2_cb(uv_work_t* req) { 153 1.1 christos ASSERT(0 && "work2_cb called"); 154 1.1 christos } 155 1.1 christos 156 1.1 christos 157 1.1 christos static void done2_cb(uv_work_t* req, int status) { 158 1.1.1.3 christos ASSERT_EQ(status, UV_ECANCELED); 159 1.1 christos done2_cb_called++; 160 1.1 christos } 161 1.1 christos 162 1.1 christos 163 1.1 christos static void timer_cb(uv_timer_t* handle) { 164 1.1 christos struct cancel_info* ci; 165 1.1 christos uv_req_t* req; 166 1.1 christos unsigned i; 167 1.1 christos 168 1.1 christos ci = container_of(handle, struct cancel_info, timer_handle); 169 1.1 christos 170 1.1 christos for (i = 0; i < ci->nreqs; i++) { 171 1.1 christos req = (uv_req_t*) ((char*) ci->reqs + i * ci->stride); 172 1.1.1.3 christos ASSERT(known_broken(req) || 0 == uv_cancel(req)); 173 1.1 christos } 174 1.1 christos 175 1.1 christos uv_close((uv_handle_t*) &ci->timer_handle, NULL); 176 1.1 christos unblock_threadpool(); 177 1.1 christos timer_cb_called++; 178 1.1 christos } 179 1.1 christos 180 1.1 christos 181 1.1 christos static void nop_done_cb(uv_work_t* req, int status) { 182 1.1.1.3 christos ASSERT_EQ(status, UV_ECANCELED); 183 1.1 christos done_cb_called++; 184 1.1 christos } 185 1.1 christos 186 1.1 christos 187 1.1 christos static void nop_random_cb(uv_random_t* req, int status, void* buf, size_t len) { 188 1.1 christos struct random_info* ri; 189 1.1 christos 190 1.1 christos ri = container_of(req, struct random_info, random_req); 191 1.1 christos 192 1.1.1.3 christos ASSERT_EQ(status, UV_ECANCELED); 193 1.1.1.3 christos ASSERT_PTR_EQ(buf, (void*) ri->buf); 194 1.1.1.3 christos ASSERT_EQ(len, sizeof(ri->buf)); 195 1.1 christos 196 1.1 christos done_cb_called++; 197 1.1 christos } 198 1.1 christos 199 1.1 christos 200 1.1 christos TEST_IMPL(threadpool_cancel_getaddrinfo) { 201 1.1 christos uv_getaddrinfo_t reqs[4]; 202 1.1 christos struct cancel_info ci; 203 1.1 christos struct addrinfo hints; 204 1.1 christos uv_loop_t* loop; 205 1.1 christos int r; 206 1.1 christos 207 1.1 christos INIT_CANCEL_INFO(&ci, reqs); 208 1.1 christos loop = uv_default_loop(); 209 1.1 christos saturate_threadpool(); 210 1.1 christos 211 1.1 christos r = uv_getaddrinfo(loop, reqs + 0, getaddrinfo_cb, "fail", NULL, NULL); 212 1.1.1.3 christos ASSERT_OK(r); 213 1.1 christos 214 1.1 christos r = uv_getaddrinfo(loop, reqs + 1, getaddrinfo_cb, NULL, "fail", NULL); 215 1.1.1.3 christos ASSERT_OK(r); 216 1.1 christos 217 1.1 christos r = uv_getaddrinfo(loop, reqs + 2, getaddrinfo_cb, "fail", "fail", NULL); 218 1.1.1.3 christos ASSERT_OK(r); 219 1.1 christos 220 1.1 christos r = uv_getaddrinfo(loop, reqs + 3, getaddrinfo_cb, "fail", NULL, &hints); 221 1.1.1.3 christos ASSERT_OK(r); 222 1.1 christos 223 1.1.1.3 christos ASSERT_OK(uv_timer_init(loop, &ci.timer_handle)); 224 1.1.1.3 christos ASSERT_OK(uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); 225 1.1.1.3 christos ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); 226 1.1.1.3 christos ASSERT_EQ(1, timer_cb_called); 227 1.1 christos 228 1.1.1.3 christos MAKE_VALGRIND_HAPPY(loop); 229 1.1 christos return 0; 230 1.1 christos } 231 1.1 christos 232 1.1 christos 233 1.1 christos TEST_IMPL(threadpool_cancel_getnameinfo) { 234 1.1 christos uv_getnameinfo_t reqs[4]; 235 1.1 christos struct sockaddr_in addr4; 236 1.1 christos struct cancel_info ci; 237 1.1 christos uv_loop_t* loop; 238 1.1 christos int r; 239 1.1 christos 240 1.1 christos r = uv_ip4_addr("127.0.0.1", 80, &addr4); 241 1.1.1.3 christos ASSERT_OK(r); 242 1.1 christos 243 1.1 christos INIT_CANCEL_INFO(&ci, reqs); 244 1.1 christos loop = uv_default_loop(); 245 1.1 christos saturate_threadpool(); 246 1.1 christos 247 1.1 christos r = uv_getnameinfo(loop, reqs + 0, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); 248 1.1.1.3 christos ASSERT_OK(r); 249 1.1 christos 250 1.1 christos r = uv_getnameinfo(loop, reqs + 1, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); 251 1.1.1.3 christos ASSERT_OK(r); 252 1.1 christos 253 1.1 christos r = uv_getnameinfo(loop, reqs + 2, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); 254 1.1.1.3 christos ASSERT_OK(r); 255 1.1 christos 256 1.1 christos r = uv_getnameinfo(loop, reqs + 3, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); 257 1.1.1.3 christos ASSERT_OK(r); 258 1.1 christos 259 1.1.1.3 christos ASSERT_OK(uv_timer_init(loop, &ci.timer_handle)); 260 1.1.1.3 christos ASSERT_OK(uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); 261 1.1.1.3 christos ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); 262 1.1.1.3 christos ASSERT_EQ(1, timer_cb_called); 263 1.1 christos 264 1.1.1.3 christos MAKE_VALGRIND_HAPPY(loop); 265 1.1 christos return 0; 266 1.1 christos } 267 1.1 christos 268 1.1 christos 269 1.1 christos TEST_IMPL(threadpool_cancel_random) { 270 1.1 christos struct random_info req; 271 1.1 christos uv_loop_t* loop; 272 1.1 christos 273 1.1 christos saturate_threadpool(); 274 1.1 christos loop = uv_default_loop(); 275 1.1.1.3 christos ASSERT_OK(uv_random(loop, 276 1.1.1.3 christos &req.random_req, 277 1.1.1.3 christos &req.buf, 278 1.1.1.3 christos sizeof(req.buf), 279 1.1.1.3 christos 0, 280 1.1.1.3 christos nop_random_cb)); 281 1.1.1.3 christos ASSERT_OK(uv_cancel((uv_req_t*) &req)); 282 1.1.1.3 christos ASSERT_OK(done_cb_called); 283 1.1 christos unblock_threadpool(); 284 1.1.1.3 christos ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); 285 1.1.1.3 christos ASSERT_EQ(1, done_cb_called); 286 1.1 christos 287 1.1.1.3 christos MAKE_VALGRIND_HAPPY(loop); 288 1.1 christos return 0; 289 1.1 christos } 290 1.1 christos 291 1.1 christos 292 1.1 christos TEST_IMPL(threadpool_cancel_work) { 293 1.1 christos struct cancel_info ci; 294 1.1 christos uv_work_t reqs[16]; 295 1.1 christos uv_loop_t* loop; 296 1.1 christos unsigned i; 297 1.1 christos 298 1.1 christos INIT_CANCEL_INFO(&ci, reqs); 299 1.1 christos loop = uv_default_loop(); 300 1.1 christos saturate_threadpool(); 301 1.1 christos 302 1.1 christos for (i = 0; i < ARRAY_SIZE(reqs); i++) 303 1.1.1.3 christos ASSERT_OK(uv_queue_work(loop, reqs + i, work2_cb, done2_cb)); 304 1.1 christos 305 1.1.1.3 christos ASSERT_OK(uv_timer_init(loop, &ci.timer_handle)); 306 1.1.1.3 christos ASSERT_OK(uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); 307 1.1.1.3 christos ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); 308 1.1.1.3 christos ASSERT_EQ(1, timer_cb_called); 309 1.1.1.3 christos ASSERT_EQ(ARRAY_SIZE(reqs), done2_cb_called); 310 1.1 christos 311 1.1.1.3 christos MAKE_VALGRIND_HAPPY(loop); 312 1.1 christos return 0; 313 1.1 christos } 314 1.1 christos 315 1.1 christos 316 1.1 christos TEST_IMPL(threadpool_cancel_fs) { 317 1.1 christos struct cancel_info ci; 318 1.1 christos uv_fs_t reqs[26]; 319 1.1 christos uv_loop_t* loop; 320 1.1 christos unsigned n; 321 1.1 christos uv_buf_t iov; 322 1.1 christos 323 1.1 christos INIT_CANCEL_INFO(&ci, reqs); 324 1.1 christos loop = uv_default_loop(); 325 1.1 christos saturate_threadpool(); 326 1.1 christos iov = uv_buf_init(NULL, 0); 327 1.1 christos 328 1.1 christos /* Needs to match ARRAY_SIZE(fs_reqs). */ 329 1.1 christos n = 0; 330 1.1.1.3 christos ASSERT_OK(uv_fs_chmod(loop, reqs + n++, "/", 0, fs_cb)); 331 1.1.1.3 christos ASSERT_OK(uv_fs_chown(loop, reqs + n++, "/", 0, 0, fs_cb)); 332 1.1.1.3 christos ASSERT_OK(uv_fs_close(loop, reqs + n++, 0, fs_cb)); 333 1.1.1.3 christos ASSERT_OK(uv_fs_fchmod(loop, reqs + n++, 0, 0, fs_cb)); 334 1.1.1.3 christos ASSERT_OK(uv_fs_fchown(loop, reqs + n++, 0, 0, 0, fs_cb)); 335 1.1.1.3 christos ASSERT_OK(uv_fs_fdatasync(loop, reqs + n++, 0, fs_cb)); 336 1.1.1.3 christos ASSERT_OK(uv_fs_fstat(loop, reqs + n++, 0, fs_cb)); 337 1.1.1.3 christos ASSERT_OK(uv_fs_fsync(loop, reqs + n++, 0, fs_cb)); 338 1.1.1.3 christos ASSERT_OK(uv_fs_ftruncate(loop, reqs + n++, 0, 0, fs_cb)); 339 1.1.1.3 christos ASSERT_OK(uv_fs_futime(loop, reqs + n++, 0, 0, 0, fs_cb)); 340 1.1.1.3 christos ASSERT_OK(uv_fs_link(loop, reqs + n++, "/", "/", fs_cb)); 341 1.1.1.3 christos ASSERT_OK(uv_fs_lstat(loop, reqs + n++, "/", fs_cb)); 342 1.1.1.3 christos ASSERT_OK(uv_fs_mkdir(loop, reqs + n++, "/", 0, fs_cb)); 343 1.1.1.3 christos ASSERT_OK(uv_fs_open(loop, reqs + n++, "/", 0, 0, fs_cb)); 344 1.1.1.3 christos ASSERT_OK(uv_fs_read(loop, reqs + n++, -1, &iov, 1, 0, fs_cb)); 345 1.1.1.3 christos ASSERT_OK(uv_fs_scandir(loop, reqs + n++, "/", 0, fs_cb)); 346 1.1.1.3 christos ASSERT_OK(uv_fs_readlink(loop, reqs + n++, "/", fs_cb)); 347 1.1.1.3 christos ASSERT_OK(uv_fs_realpath(loop, reqs + n++, "/", fs_cb)); 348 1.1.1.3 christos ASSERT_OK(uv_fs_rename(loop, reqs + n++, "/", "/", fs_cb)); 349 1.1.1.3 christos ASSERT_OK(uv_fs_mkdir(loop, reqs + n++, "/", 0, fs_cb)); 350 1.1.1.3 christos ASSERT_OK(uv_fs_sendfile(loop, reqs + n++, 0, 0, 0, 0, fs_cb)); 351 1.1.1.3 christos ASSERT_OK(uv_fs_stat(loop, reqs + n++, "/", fs_cb)); 352 1.1.1.3 christos ASSERT_OK(uv_fs_symlink(loop, reqs + n++, "/", "/", 0, fs_cb)); 353 1.1.1.3 christos ASSERT_OK(uv_fs_unlink(loop, reqs + n++, "/", fs_cb)); 354 1.1.1.3 christos ASSERT_OK(uv_fs_utime(loop, reqs + n++, "/", 0, 0, fs_cb)); 355 1.1.1.3 christos ASSERT_OK(uv_fs_write(loop, reqs + n++, -1, &iov, 1, 0, fs_cb)); 356 1.1.1.3 christos ASSERT_EQ(n, ARRAY_SIZE(reqs)); 357 1.1.1.3 christos 358 1.1.1.3 christos ASSERT_OK(uv_timer_init(loop, &ci.timer_handle)); 359 1.1.1.3 christos ASSERT_OK(uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); 360 1.1.1.3 christos ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); 361 1.1.1.3 christos ASSERT_EQ(n, fs_cb_called); 362 1.1.1.3 christos ASSERT_EQ(1, timer_cb_called); 363 1.1 christos 364 1.1 christos 365 1.1.1.3 christos MAKE_VALGRIND_HAPPY(loop); 366 1.1 christos return 0; 367 1.1 christos } 368 1.1 christos 369 1.1 christos 370 1.1 christos TEST_IMPL(threadpool_cancel_single) { 371 1.1 christos uv_loop_t* loop; 372 1.1 christos uv_work_t req; 373 1.1 christos 374 1.1 christos saturate_threadpool(); 375 1.1 christos loop = uv_default_loop(); 376 1.1.1.3 christos ASSERT_OK(uv_queue_work(loop, &req, (uv_work_cb) abort, nop_done_cb)); 377 1.1.1.3 christos ASSERT_OK(uv_cancel((uv_req_t*) &req)); 378 1.1.1.3 christos ASSERT_OK(done_cb_called); 379 1.1 christos unblock_threadpool(); 380 1.1.1.3 christos ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); 381 1.1.1.3 christos ASSERT_EQ(1, done_cb_called); 382 1.1.1.3 christos 383 1.1.1.3 christos MAKE_VALGRIND_HAPPY(loop); 384 1.1.1.3 christos return 0; 385 1.1.1.3 christos } 386 1.1.1.3 christos 387 1.1.1.3 christos 388 1.1.1.3 christos static void after_busy_cb(uv_work_t* req, int status) { 389 1.1.1.3 christos ASSERT_OK(status); 390 1.1.1.3 christos done_cb_called++; 391 1.1.1.3 christos } 392 1.1.1.3 christos 393 1.1.1.3 christos static void busy_cb(uv_work_t* req) { 394 1.1.1.3 christos uv_sem_post((uv_sem_t*) req->data); 395 1.1.1.3 christos /* Assume that calling uv_cancel() takes less than 10ms. */ 396 1.1.1.3 christos uv_sleep(10); 397 1.1.1.3 christos } 398 1.1.1.3 christos 399 1.1.1.3 christos TEST_IMPL(threadpool_cancel_when_busy) { 400 1.1.1.3 christos uv_sem_t sem_lock; 401 1.1.1.3 christos uv_work_t req; 402 1.1.1.3 christos 403 1.1.1.3 christos req.data = &sem_lock; 404 1.1.1.3 christos 405 1.1.1.3 christos ASSERT_OK(uv_sem_init(&sem_lock, 0)); 406 1.1.1.3 christos ASSERT_OK(uv_queue_work(uv_default_loop(), &req, busy_cb, after_busy_cb)); 407 1.1.1.3 christos 408 1.1.1.3 christos uv_sem_wait(&sem_lock); 409 1.1.1.3 christos 410 1.1.1.3 christos ASSERT_EQ(uv_cancel((uv_req_t*) &req), UV_EBUSY); 411 1.1.1.3 christos ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); 412 1.1.1.3 christos ASSERT_EQ(1, done_cb_called); 413 1.1.1.3 christos 414 1.1.1.3 christos uv_sem_destroy(&sem_lock); 415 1.1 christos 416 1.1.1.3 christos MAKE_VALGRIND_HAPPY(uv_default_loop()); 417 1.1 christos return 0; 418 1.1 christos } 419