Mercurial
comparison third_party/libuv/test/test-threadpool-cancel.c @ 160:948de3f54cea
[ThirdParty] Added libuv
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Wed, 14 Jan 2026 19:39:52 -0800 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 159:05cf9467a1c3 | 160:948de3f54cea |
|---|---|
| 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 #ifdef _WIN32 | |
| 26 # define putenv _putenv | |
| 27 #endif | |
| 28 | |
| 29 #define INIT_CANCEL_INFO(ci, what) \ | |
| 30 do { \ | |
| 31 (ci)->reqs = (what); \ | |
| 32 (ci)->nreqs = ARRAY_SIZE(what); \ | |
| 33 (ci)->stride = sizeof((what)[0]); \ | |
| 34 } \ | |
| 35 while (0) | |
| 36 | |
| 37 struct cancel_info { | |
| 38 void* reqs; | |
| 39 unsigned nreqs; | |
| 40 unsigned stride; | |
| 41 uv_timer_t timer_handle; | |
| 42 }; | |
| 43 | |
| 44 struct random_info { | |
| 45 uv_random_t random_req; | |
| 46 char buf[1]; | |
| 47 }; | |
| 48 | |
| 49 static unsigned fs_cb_called; | |
| 50 static unsigned done_cb_called; | |
| 51 static unsigned done2_cb_called; | |
| 52 static unsigned timer_cb_called; | |
| 53 static uv_work_t pause_reqs[4]; | |
| 54 static uv_sem_t pause_sems[ARRAY_SIZE(pause_reqs)]; | |
| 55 | |
| 56 | |
| 57 static void work_cb(uv_work_t* req) { | |
| 58 uv_sem_wait(pause_sems + (req - pause_reqs)); | |
| 59 } | |
| 60 | |
| 61 | |
| 62 static void done_cb(uv_work_t* req, int status) { | |
| 63 uv_sem_destroy(pause_sems + (req - pause_reqs)); | |
| 64 } | |
| 65 | |
| 66 | |
| 67 static void saturate_threadpool(void) { | |
| 68 uv_loop_t* loop; | |
| 69 char buf[64]; | |
| 70 size_t i; | |
| 71 | |
| 72 snprintf(buf, | |
| 73 sizeof(buf), | |
| 74 "UV_THREADPOOL_SIZE=%lu", | |
| 75 (unsigned long)ARRAY_SIZE(pause_reqs)); | |
| 76 putenv(buf); | |
| 77 | |
| 78 loop = uv_default_loop(); | |
| 79 for (i = 0; i < ARRAY_SIZE(pause_reqs); i += 1) { | |
| 80 ASSERT_OK(uv_sem_init(pause_sems + i, 0)); | |
| 81 ASSERT_OK(uv_queue_work(loop, pause_reqs + i, work_cb, done_cb)); | |
| 82 } | |
| 83 } | |
| 84 | |
| 85 | |
| 86 static void unblock_threadpool(void) { | |
| 87 size_t i; | |
| 88 | |
| 89 for (i = 0; i < ARRAY_SIZE(pause_reqs); i += 1) | |
| 90 uv_sem_post(pause_sems + i); | |
| 91 } | |
| 92 | |
| 93 | |
| 94 static int known_broken(uv_req_t* req) { | |
| 95 if (req->type != UV_FS) | |
| 96 return 0; | |
| 97 | |
| 98 #ifdef __linux__ | |
| 99 /* TODO(bnoordhuis) make cancellation work with io_uring */ | |
| 100 switch (((uv_fs_t*) req)->fs_type) { | |
| 101 case UV_FS_CLOSE: | |
| 102 case UV_FS_FDATASYNC: | |
| 103 case UV_FS_FSTAT: | |
| 104 case UV_FS_FSYNC: | |
| 105 case UV_FS_LINK: | |
| 106 case UV_FS_LSTAT: | |
| 107 case UV_FS_MKDIR: | |
| 108 case UV_FS_OPEN: | |
| 109 case UV_FS_READ: | |
| 110 case UV_FS_RENAME: | |
| 111 case UV_FS_STAT: | |
| 112 case UV_FS_SYMLINK: | |
| 113 case UV_FS_WRITE: | |
| 114 case UV_FS_UNLINK: | |
| 115 return 1; | |
| 116 default: /* Squelch -Wswitch warnings. */ | |
| 117 break; | |
| 118 } | |
| 119 #endif | |
| 120 | |
| 121 return 0; | |
| 122 } | |
| 123 | |
| 124 | |
| 125 static void fs_cb(uv_fs_t* req) { | |
| 126 ASSERT_NE(known_broken((uv_req_t*) req) || \ | |
| 127 req->result == UV_ECANCELED, 0); | |
| 128 uv_fs_req_cleanup(req); | |
| 129 fs_cb_called++; | |
| 130 } | |
| 131 | |
| 132 | |
| 133 static void getaddrinfo_cb(uv_getaddrinfo_t* req, | |
| 134 int status, | |
| 135 struct addrinfo* res) { | |
| 136 ASSERT_EQ(status, UV_EAI_CANCELED); | |
| 137 ASSERT_NULL(res); | |
| 138 uv_freeaddrinfo(res); /* Should not crash. */ | |
| 139 } | |
| 140 | |
| 141 | |
| 142 static void getnameinfo_cb(uv_getnameinfo_t* handle, | |
| 143 int status, | |
| 144 const char* hostname, | |
| 145 const char* service) { | |
| 146 ASSERT_EQ(status, UV_EAI_CANCELED); | |
| 147 ASSERT_NULL(hostname); | |
| 148 ASSERT_NULL(service); | |
| 149 } | |
| 150 | |
| 151 | |
| 152 static void work2_cb(uv_work_t* req) { | |
| 153 ASSERT(0 && "work2_cb called"); | |
| 154 } | |
| 155 | |
| 156 | |
| 157 static void done2_cb(uv_work_t* req, int status) { | |
| 158 ASSERT_EQ(status, UV_ECANCELED); | |
| 159 done2_cb_called++; | |
| 160 } | |
| 161 | |
| 162 | |
| 163 static void timer_cb(uv_timer_t* handle) { | |
| 164 struct cancel_info* ci; | |
| 165 uv_req_t* req; | |
| 166 unsigned i; | |
| 167 | |
| 168 ci = container_of(handle, struct cancel_info, timer_handle); | |
| 169 | |
| 170 for (i = 0; i < ci->nreqs; i++) { | |
| 171 req = (uv_req_t*) ((char*) ci->reqs + i * ci->stride); | |
| 172 ASSERT(known_broken(req) || 0 == uv_cancel(req)); | |
| 173 } | |
| 174 | |
| 175 uv_close((uv_handle_t*) &ci->timer_handle, NULL); | |
| 176 unblock_threadpool(); | |
| 177 timer_cb_called++; | |
| 178 } | |
| 179 | |
| 180 | |
| 181 static void nop_done_cb(uv_work_t* req, int status) { | |
| 182 ASSERT_EQ(status, UV_ECANCELED); | |
| 183 done_cb_called++; | |
| 184 } | |
| 185 | |
| 186 | |
| 187 static void nop_random_cb(uv_random_t* req, int status, void* buf, size_t len) { | |
| 188 struct random_info* ri; | |
| 189 | |
| 190 ri = container_of(req, struct random_info, random_req); | |
| 191 | |
| 192 ASSERT_EQ(status, UV_ECANCELED); | |
| 193 ASSERT_PTR_EQ(buf, (void*) ri->buf); | |
| 194 ASSERT_EQ(len, sizeof(ri->buf)); | |
| 195 | |
| 196 done_cb_called++; | |
| 197 } | |
| 198 | |
| 199 | |
| 200 TEST_IMPL(threadpool_cancel_getaddrinfo) { | |
| 201 uv_getaddrinfo_t reqs[4]; | |
| 202 struct cancel_info ci; | |
| 203 struct addrinfo hints; | |
| 204 uv_loop_t* loop; | |
| 205 int r; | |
| 206 | |
| 207 INIT_CANCEL_INFO(&ci, reqs); | |
| 208 loop = uv_default_loop(); | |
| 209 saturate_threadpool(); | |
| 210 | |
| 211 r = uv_getaddrinfo(loop, reqs + 0, getaddrinfo_cb, "fail", NULL, NULL); | |
| 212 ASSERT_OK(r); | |
| 213 | |
| 214 r = uv_getaddrinfo(loop, reqs + 1, getaddrinfo_cb, NULL, "fail", NULL); | |
| 215 ASSERT_OK(r); | |
| 216 | |
| 217 r = uv_getaddrinfo(loop, reqs + 2, getaddrinfo_cb, "fail", "fail", NULL); | |
| 218 ASSERT_OK(r); | |
| 219 | |
| 220 r = uv_getaddrinfo(loop, reqs + 3, getaddrinfo_cb, "fail", NULL, &hints); | |
| 221 ASSERT_OK(r); | |
| 222 | |
| 223 ASSERT_OK(uv_timer_init(loop, &ci.timer_handle)); | |
| 224 ASSERT_OK(uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); | |
| 225 ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); | |
| 226 ASSERT_EQ(1, timer_cb_called); | |
| 227 | |
| 228 MAKE_VALGRIND_HAPPY(loop); | |
| 229 return 0; | |
| 230 } | |
| 231 | |
| 232 | |
| 233 TEST_IMPL(threadpool_cancel_getnameinfo) { | |
| 234 uv_getnameinfo_t reqs[4]; | |
| 235 struct sockaddr_in addr4; | |
| 236 struct cancel_info ci; | |
| 237 uv_loop_t* loop; | |
| 238 int r; | |
| 239 | |
| 240 r = uv_ip4_addr("127.0.0.1", 80, &addr4); | |
| 241 ASSERT_OK(r); | |
| 242 | |
| 243 INIT_CANCEL_INFO(&ci, reqs); | |
| 244 loop = uv_default_loop(); | |
| 245 saturate_threadpool(); | |
| 246 | |
| 247 r = uv_getnameinfo(loop, reqs + 0, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); | |
| 248 ASSERT_OK(r); | |
| 249 | |
| 250 r = uv_getnameinfo(loop, reqs + 1, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); | |
| 251 ASSERT_OK(r); | |
| 252 | |
| 253 r = uv_getnameinfo(loop, reqs + 2, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); | |
| 254 ASSERT_OK(r); | |
| 255 | |
| 256 r = uv_getnameinfo(loop, reqs + 3, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); | |
| 257 ASSERT_OK(r); | |
| 258 | |
| 259 ASSERT_OK(uv_timer_init(loop, &ci.timer_handle)); | |
| 260 ASSERT_OK(uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); | |
| 261 ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); | |
| 262 ASSERT_EQ(1, timer_cb_called); | |
| 263 | |
| 264 MAKE_VALGRIND_HAPPY(loop); | |
| 265 return 0; | |
| 266 } | |
| 267 | |
| 268 | |
| 269 TEST_IMPL(threadpool_cancel_random) { | |
| 270 struct random_info req; | |
| 271 uv_loop_t* loop; | |
| 272 | |
| 273 saturate_threadpool(); | |
| 274 loop = uv_default_loop(); | |
| 275 ASSERT_OK(uv_random(loop, | |
| 276 &req.random_req, | |
| 277 &req.buf, | |
| 278 sizeof(req.buf), | |
| 279 0, | |
| 280 nop_random_cb)); | |
| 281 ASSERT_OK(uv_cancel((uv_req_t*) &req)); | |
| 282 ASSERT_OK(done_cb_called); | |
| 283 unblock_threadpool(); | |
| 284 ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); | |
| 285 ASSERT_EQ(1, done_cb_called); | |
| 286 | |
| 287 MAKE_VALGRIND_HAPPY(loop); | |
| 288 return 0; | |
| 289 } | |
| 290 | |
| 291 | |
| 292 TEST_IMPL(threadpool_cancel_work) { | |
| 293 struct cancel_info ci; | |
| 294 uv_work_t reqs[16]; | |
| 295 uv_loop_t* loop; | |
| 296 unsigned i; | |
| 297 | |
| 298 INIT_CANCEL_INFO(&ci, reqs); | |
| 299 loop = uv_default_loop(); | |
| 300 saturate_threadpool(); | |
| 301 | |
| 302 for (i = 0; i < ARRAY_SIZE(reqs); i++) | |
| 303 ASSERT_OK(uv_queue_work(loop, reqs + i, work2_cb, done2_cb)); | |
| 304 | |
| 305 ASSERT_OK(uv_timer_init(loop, &ci.timer_handle)); | |
| 306 ASSERT_OK(uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); | |
| 307 ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); | |
| 308 ASSERT_EQ(1, timer_cb_called); | |
| 309 ASSERT_EQ(ARRAY_SIZE(reqs), done2_cb_called); | |
| 310 | |
| 311 MAKE_VALGRIND_HAPPY(loop); | |
| 312 return 0; | |
| 313 } | |
| 314 | |
| 315 | |
| 316 TEST_IMPL(threadpool_cancel_fs) { | |
| 317 struct cancel_info ci; | |
| 318 uv_fs_t reqs[26]; | |
| 319 uv_loop_t* loop; | |
| 320 unsigned n; | |
| 321 uv_buf_t iov; | |
| 322 | |
| 323 INIT_CANCEL_INFO(&ci, reqs); | |
| 324 loop = uv_default_loop(); | |
| 325 saturate_threadpool(); | |
| 326 iov = uv_buf_init(NULL, 0); | |
| 327 | |
| 328 /* Needs to match ARRAY_SIZE(fs_reqs). */ | |
| 329 n = 0; | |
| 330 ASSERT_OK(uv_fs_chmod(loop, reqs + n++, "/", 0, fs_cb)); | |
| 331 ASSERT_OK(uv_fs_chown(loop, reqs + n++, "/", 0, 0, fs_cb)); | |
| 332 ASSERT_OK(uv_fs_close(loop, reqs + n++, 0, fs_cb)); | |
| 333 ASSERT_OK(uv_fs_fchmod(loop, reqs + n++, 0, 0, fs_cb)); | |
| 334 ASSERT_OK(uv_fs_fchown(loop, reqs + n++, 0, 0, 0, fs_cb)); | |
| 335 ASSERT_OK(uv_fs_fdatasync(loop, reqs + n++, 0, fs_cb)); | |
| 336 ASSERT_OK(uv_fs_fstat(loop, reqs + n++, 0, fs_cb)); | |
| 337 ASSERT_OK(uv_fs_fsync(loop, reqs + n++, 0, fs_cb)); | |
| 338 ASSERT_OK(uv_fs_ftruncate(loop, reqs + n++, 0, 0, fs_cb)); | |
| 339 ASSERT_OK(uv_fs_futime(loop, reqs + n++, 0, 0, 0, fs_cb)); | |
| 340 ASSERT_OK(uv_fs_link(loop, reqs + n++, "/", "/", fs_cb)); | |
| 341 ASSERT_OK(uv_fs_lstat(loop, reqs + n++, "/", fs_cb)); | |
| 342 ASSERT_OK(uv_fs_mkdir(loop, reqs + n++, "/", 0, fs_cb)); | |
| 343 ASSERT_OK(uv_fs_open(loop, reqs + n++, "/", 0, 0, fs_cb)); | |
| 344 ASSERT_OK(uv_fs_read(loop, reqs + n++, -1, &iov, 1, 0, fs_cb)); | |
| 345 ASSERT_OK(uv_fs_scandir(loop, reqs + n++, "/", 0, fs_cb)); | |
| 346 ASSERT_OK(uv_fs_readlink(loop, reqs + n++, "/", fs_cb)); | |
| 347 ASSERT_OK(uv_fs_realpath(loop, reqs + n++, "/", fs_cb)); | |
| 348 ASSERT_OK(uv_fs_rename(loop, reqs + n++, "/", "/", fs_cb)); | |
| 349 ASSERT_OK(uv_fs_mkdir(loop, reqs + n++, "/", 0, fs_cb)); | |
| 350 ASSERT_OK(uv_fs_sendfile(loop, reqs + n++, 0, 0, 0, 0, fs_cb)); | |
| 351 ASSERT_OK(uv_fs_stat(loop, reqs + n++, "/", fs_cb)); | |
| 352 ASSERT_OK(uv_fs_symlink(loop, reqs + n++, "/", "/", 0, fs_cb)); | |
| 353 ASSERT_OK(uv_fs_unlink(loop, reqs + n++, "/", fs_cb)); | |
| 354 ASSERT_OK(uv_fs_utime(loop, reqs + n++, "/", 0, 0, fs_cb)); | |
| 355 ASSERT_OK(uv_fs_write(loop, reqs + n++, -1, &iov, 1, 0, fs_cb)); | |
| 356 ASSERT_EQ(n, ARRAY_SIZE(reqs)); | |
| 357 | |
| 358 ASSERT_OK(uv_timer_init(loop, &ci.timer_handle)); | |
| 359 ASSERT_OK(uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); | |
| 360 ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); | |
| 361 ASSERT_EQ(n, fs_cb_called); | |
| 362 ASSERT_EQ(1, timer_cb_called); | |
| 363 | |
| 364 | |
| 365 MAKE_VALGRIND_HAPPY(loop); | |
| 366 return 0; | |
| 367 } | |
| 368 | |
| 369 | |
| 370 TEST_IMPL(threadpool_cancel_single) { | |
| 371 uv_loop_t* loop; | |
| 372 uv_work_t req; | |
| 373 | |
| 374 saturate_threadpool(); | |
| 375 loop = uv_default_loop(); | |
| 376 ASSERT_OK(uv_queue_work(loop, &req, (uv_work_cb) abort, nop_done_cb)); | |
| 377 ASSERT_OK(uv_cancel((uv_req_t*) &req)); | |
| 378 ASSERT_OK(done_cb_called); | |
| 379 unblock_threadpool(); | |
| 380 ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); | |
| 381 ASSERT_EQ(1, done_cb_called); | |
| 382 | |
| 383 MAKE_VALGRIND_HAPPY(loop); | |
| 384 return 0; | |
| 385 } | |
| 386 | |
| 387 | |
| 388 static void after_busy_cb(uv_work_t* req, int status) { | |
| 389 ASSERT_OK(status); | |
| 390 done_cb_called++; | |
| 391 } | |
| 392 | |
| 393 static void busy_cb(uv_work_t* req) { | |
| 394 uv_sem_post((uv_sem_t*) req->data); | |
| 395 /* Assume that calling uv_cancel() takes less than 10ms. */ | |
| 396 uv_sleep(10); | |
| 397 } | |
| 398 | |
| 399 TEST_IMPL(threadpool_cancel_when_busy) { | |
| 400 uv_sem_t sem_lock; | |
| 401 uv_work_t req; | |
| 402 | |
| 403 req.data = &sem_lock; | |
| 404 | |
| 405 ASSERT_OK(uv_sem_init(&sem_lock, 0)); | |
| 406 ASSERT_OK(uv_queue_work(uv_default_loop(), &req, busy_cb, after_busy_cb)); | |
| 407 | |
| 408 uv_sem_wait(&sem_lock); | |
| 409 | |
| 410 ASSERT_EQ(uv_cancel((uv_req_t*) &req), UV_EBUSY); | |
| 411 ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); | |
| 412 ASSERT_EQ(1, done_cb_called); | |
| 413 | |
| 414 uv_sem_destroy(&sem_lock); | |
| 415 | |
| 416 MAKE_VALGRIND_HAPPY(uv_default_loop()); | |
| 417 return 0; | |
| 418 } |