ares_threads.c (13475B)
1 /* MIT License 2 * 3 * Copyright (c) 2023 Brad House 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a copy 6 * of this software and associated documentation files (the "Software"), to deal 7 * in the Software without restriction, including without limitation the rights 8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 * copies of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 * 24 * SPDX-License-Identifier: MIT 25 */ 26 #include "ares_private.h" 27 28 #ifdef CARES_THREADS 29 # ifdef _WIN32 30 31 struct ares_thread_mutex { 32 CRITICAL_SECTION mutex; 33 }; 34 35 ares_thread_mutex_t *ares_thread_mutex_create(void) 36 { 37 ares_thread_mutex_t *mut = ares_malloc_zero(sizeof(*mut)); 38 if (mut == NULL) { 39 return NULL; 40 } 41 42 InitializeCriticalSection(&mut->mutex); 43 return mut; 44 } 45 46 void ares_thread_mutex_destroy(ares_thread_mutex_t *mut) 47 { 48 if (mut == NULL) { 49 return; 50 } 51 DeleteCriticalSection(&mut->mutex); 52 ares_free(mut); 53 } 54 55 void ares_thread_mutex_lock(ares_thread_mutex_t *mut) 56 { 57 if (mut == NULL) { 58 return; 59 } 60 EnterCriticalSection(&mut->mutex); 61 } 62 63 void ares_thread_mutex_unlock(ares_thread_mutex_t *mut) 64 { 65 if (mut == NULL) { 66 return; 67 } 68 LeaveCriticalSection(&mut->mutex); 69 } 70 71 struct ares_thread_cond { 72 CONDITION_VARIABLE cond; 73 }; 74 75 ares_thread_cond_t *ares_thread_cond_create(void) 76 { 77 ares_thread_cond_t *cond = ares_malloc_zero(sizeof(*cond)); 78 if (cond == NULL) { 79 return NULL; 80 } 81 InitializeConditionVariable(&cond->cond); 82 return cond; 83 } 84 85 void ares_thread_cond_destroy(ares_thread_cond_t *cond) 86 { 87 if (cond == NULL) { 88 return; 89 } 90 ares_free(cond); 91 } 92 93 void ares_thread_cond_signal(ares_thread_cond_t *cond) 94 { 95 if (cond == NULL) { 96 return; 97 } 98 WakeConditionVariable(&cond->cond); 99 } 100 101 void ares_thread_cond_broadcast(ares_thread_cond_t *cond) 102 { 103 if (cond == NULL) { 104 return; 105 } 106 WakeAllConditionVariable(&cond->cond); 107 } 108 109 ares_status_t ares_thread_cond_wait(ares_thread_cond_t *cond, 110 ares_thread_mutex_t *mut) 111 { 112 if (cond == NULL || mut == NULL) { 113 return ARES_EFORMERR; 114 } 115 116 SleepConditionVariableCS(&cond->cond, &mut->mutex, INFINITE); 117 return ARES_SUCCESS; 118 } 119 120 ares_status_t ares_thread_cond_timedwait(ares_thread_cond_t *cond, 121 ares_thread_mutex_t *mut, 122 unsigned long timeout_ms) 123 { 124 if (cond == NULL || mut == NULL) { 125 return ARES_EFORMERR; 126 } 127 128 if (!SleepConditionVariableCS(&cond->cond, &mut->mutex, timeout_ms)) { 129 return ARES_ETIMEOUT; 130 } 131 132 return ARES_SUCCESS; 133 } 134 135 struct ares_thread { 136 HANDLE thread; 137 DWORD id; 138 139 void *(*func)(void *arg); 140 void *arg; 141 void *rv; 142 }; 143 144 /* Wrap for pthread compatibility */ 145 static DWORD WINAPI ares_thread_func(LPVOID lpParameter) 146 { 147 ares_thread_t *thread = lpParameter; 148 149 thread->rv = thread->func(thread->arg); 150 return 0; 151 } 152 153 ares_status_t ares_thread_create(ares_thread_t **thread, 154 ares_thread_func_t func, void *arg) 155 { 156 ares_thread_t *thr = NULL; 157 158 if (func == NULL || thread == NULL) { 159 return ARES_EFORMERR; 160 } 161 162 thr = ares_malloc_zero(sizeof(*thr)); 163 if (thr == NULL) { 164 return ARES_ENOMEM; 165 } 166 167 thr->func = func; 168 thr->arg = arg; 169 thr->thread = CreateThread(NULL, 0, ares_thread_func, thr, 0, &thr->id); 170 if (thr->thread == NULL) { 171 ares_free(thr); 172 return ARES_ESERVFAIL; 173 } 174 175 *thread = thr; 176 return ARES_SUCCESS; 177 } 178 179 ares_status_t ares_thread_join(ares_thread_t *thread, void **rv) 180 { 181 ares_status_t status = ARES_SUCCESS; 182 183 if (thread == NULL) { 184 return ARES_EFORMERR; 185 } 186 187 if (WaitForSingleObject(thread->thread, INFINITE) != WAIT_OBJECT_0) { 188 status = ARES_ENOTFOUND; 189 } else { 190 CloseHandle(thread->thread); 191 } 192 193 if (status == ARES_SUCCESS && rv != NULL) { 194 *rv = thread->rv; 195 } 196 ares_free(thread); 197 198 return status; 199 } 200 201 # else /* !WIN32 == PTHREAD */ 202 # include <pthread.h> 203 204 /* for clock_gettime() */ 205 # ifdef HAVE_TIME_H 206 # include <time.h> 207 # endif 208 209 /* for gettimeofday() */ 210 # ifdef HAVE_SYS_TIME_H 211 # include <sys/time.h> 212 # endif 213 214 struct ares_thread_mutex { 215 pthread_mutex_t mutex; 216 }; 217 218 ares_thread_mutex_t *ares_thread_mutex_create(void) 219 { 220 pthread_mutexattr_t attr; 221 ares_thread_mutex_t *mut = ares_malloc_zero(sizeof(*mut)); 222 if (mut == NULL) { 223 return NULL; 224 } 225 226 if (pthread_mutexattr_init(&attr) != 0) { 227 ares_free(mut); /* LCOV_EXCL_LINE: UntestablePath */ 228 return NULL; /* LCOV_EXCL_LINE: UntestablePath */ 229 } 230 231 if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) { 232 goto fail; /* LCOV_EXCL_LINE: UntestablePath */ 233 } 234 235 if (pthread_mutex_init(&mut->mutex, &attr) != 0) { 236 goto fail; /* LCOV_EXCL_LINE: UntestablePath */ 237 } 238 239 pthread_mutexattr_destroy(&attr); 240 return mut; 241 242 /* LCOV_EXCL_START: UntestablePath */ 243 fail: 244 pthread_mutexattr_destroy(&attr); 245 ares_free(mut); 246 return NULL; 247 /* LCOV_EXCL_STOP */ 248 } 249 250 void ares_thread_mutex_destroy(ares_thread_mutex_t *mut) 251 { 252 if (mut == NULL) { 253 return; 254 } 255 pthread_mutex_destroy(&mut->mutex); 256 ares_free(mut); 257 } 258 259 void ares_thread_mutex_lock(ares_thread_mutex_t *mut) 260 { 261 if (mut == NULL) { 262 return; 263 } 264 pthread_mutex_lock(&mut->mutex); 265 } 266 267 void ares_thread_mutex_unlock(ares_thread_mutex_t *mut) 268 { 269 if (mut == NULL) { 270 return; 271 } 272 pthread_mutex_unlock(&mut->mutex); 273 } 274 275 struct ares_thread_cond { 276 pthread_cond_t cond; 277 }; 278 279 ares_thread_cond_t *ares_thread_cond_create(void) 280 { 281 ares_thread_cond_t *cond = ares_malloc_zero(sizeof(*cond)); 282 if (cond == NULL) { 283 return NULL; 284 } 285 pthread_cond_init(&cond->cond, NULL); 286 return cond; 287 } 288 289 void ares_thread_cond_destroy(ares_thread_cond_t *cond) 290 { 291 if (cond == NULL) { 292 return; 293 } 294 pthread_cond_destroy(&cond->cond); 295 ares_free(cond); 296 } 297 298 void ares_thread_cond_signal(ares_thread_cond_t *cond) 299 { 300 if (cond == NULL) { 301 return; 302 } 303 pthread_cond_signal(&cond->cond); 304 } 305 306 void ares_thread_cond_broadcast(ares_thread_cond_t *cond) 307 { 308 if (cond == NULL) { 309 return; 310 } 311 pthread_cond_broadcast(&cond->cond); 312 } 313 314 ares_status_t ares_thread_cond_wait(ares_thread_cond_t *cond, 315 ares_thread_mutex_t *mut) 316 { 317 if (cond == NULL || mut == NULL) { 318 return ARES_EFORMERR; 319 } 320 321 pthread_cond_wait(&cond->cond, &mut->mutex); 322 return ARES_SUCCESS; 323 } 324 325 static void ares_timespec_timeout(struct timespec *ts, unsigned long add_ms) 326 { 327 # if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_REALTIME) 328 clock_gettime(CLOCK_REALTIME, ts); 329 # elif defined(HAVE_GETTIMEOFDAY) 330 struct timeval tv; 331 gettimeofday(&tv, NULL); 332 ts->tv_sec = tv.tv_sec; 333 ts->tv_nsec = tv.tv_usec * 1000; 334 # else 335 # error cannot determine current system time 336 # endif 337 338 ts->tv_sec += (time_t)(add_ms / 1000); 339 ts->tv_nsec += (long)((add_ms % 1000) * 1000000); 340 341 /* Normalize if needed */ 342 if (ts->tv_nsec >= 1000000000) { 343 ts->tv_sec += ts->tv_nsec / 1000000000; 344 ts->tv_nsec %= 1000000000; 345 } 346 } 347 348 ares_status_t ares_thread_cond_timedwait(ares_thread_cond_t *cond, 349 ares_thread_mutex_t *mut, 350 unsigned long timeout_ms) 351 { 352 struct timespec ts; 353 354 if (cond == NULL || mut == NULL) { 355 return ARES_EFORMERR; 356 } 357 358 ares_timespec_timeout(&ts, timeout_ms); 359 360 if (pthread_cond_timedwait(&cond->cond, &mut->mutex, &ts) != 0) { 361 return ARES_ETIMEOUT; 362 } 363 364 return ARES_SUCCESS; 365 } 366 367 struct ares_thread { 368 pthread_t thread; 369 }; 370 371 ares_status_t ares_thread_create(ares_thread_t **thread, 372 ares_thread_func_t func, void *arg) 373 { 374 ares_thread_t *thr = NULL; 375 376 if (func == NULL || thread == NULL) { 377 return ARES_EFORMERR; 378 } 379 380 thr = ares_malloc_zero(sizeof(*thr)); 381 if (thr == NULL) { 382 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ 383 } 384 if (pthread_create(&thr->thread, NULL, func, arg) != 0) { 385 ares_free(thr); /* LCOV_EXCL_LINE: UntestablePath */ 386 return ARES_ESERVFAIL; /* LCOV_EXCL_LINE: UntestablePath */ 387 } 388 389 *thread = thr; 390 return ARES_SUCCESS; 391 } 392 393 ares_status_t ares_thread_join(ares_thread_t *thread, void **rv) 394 { 395 void *ret = NULL; 396 ares_status_t status = ARES_SUCCESS; 397 398 if (thread == NULL) { 399 return ARES_EFORMERR; 400 } 401 402 if (pthread_join(thread->thread, &ret) != 0) { 403 status = ARES_ENOTFOUND; 404 } 405 ares_free(thread); 406 407 if (status == ARES_SUCCESS && rv != NULL) { 408 *rv = ret; 409 } 410 return status; 411 } 412 413 # endif 414 415 ares_bool_t ares_threadsafety(void) 416 { 417 return ARES_TRUE; 418 } 419 420 #else /* !CARES_THREADS */ 421 422 /* NoOp */ 423 ares_thread_mutex_t *ares_thread_mutex_create(void) 424 { 425 return NULL; 426 } 427 428 void ares_thread_mutex_destroy(ares_thread_mutex_t *mut) 429 { 430 (void)mut; 431 } 432 433 void ares_thread_mutex_lock(ares_thread_mutex_t *mut) 434 { 435 (void)mut; 436 } 437 438 void ares_thread_mutex_unlock(ares_thread_mutex_t *mut) 439 { 440 (void)mut; 441 } 442 443 ares_thread_cond_t *ares_thread_cond_create(void) 444 { 445 return NULL; 446 } 447 448 void ares_thread_cond_destroy(ares_thread_cond_t *cond) 449 { 450 (void)cond; 451 } 452 453 void ares_thread_cond_signal(ares_thread_cond_t *cond) 454 { 455 (void)cond; 456 } 457 458 void ares_thread_cond_broadcast(ares_thread_cond_t *cond) 459 { 460 (void)cond; 461 } 462 463 ares_status_t ares_thread_cond_wait(ares_thread_cond_t *cond, 464 ares_thread_mutex_t *mut) 465 { 466 (void)cond; 467 (void)mut; 468 return ARES_ENOTIMP; 469 } 470 471 ares_status_t ares_thread_cond_timedwait(ares_thread_cond_t *cond, 472 ares_thread_mutex_t *mut, 473 unsigned long timeout_ms) 474 { 475 (void)cond; 476 (void)mut; 477 (void)timeout_ms; 478 return ARES_ENOTIMP; 479 } 480 481 ares_status_t ares_thread_create(ares_thread_t **thread, 482 ares_thread_func_t func, void *arg) 483 { 484 (void)thread; 485 (void)func; 486 (void)arg; 487 return ARES_ENOTIMP; 488 } 489 490 ares_status_t ares_thread_join(ares_thread_t *thread, void **rv) 491 { 492 (void)thread; 493 (void)rv; 494 return ARES_ENOTIMP; 495 } 496 497 ares_bool_t ares_threadsafety(void) 498 { 499 return ARES_FALSE; 500 } 501 #endif 502 503 504 ares_status_t ares_channel_threading_init(ares_channel_t *channel) 505 { 506 ares_status_t status = ARES_SUCCESS; 507 508 /* Threading is optional! */ 509 if (!ares_threadsafety()) { 510 return ARES_SUCCESS; 511 } 512 513 channel->lock = ares_thread_mutex_create(); 514 if (channel->lock == NULL) { 515 status = ARES_ENOMEM; 516 goto done; 517 } 518 519 channel->cond_empty = ares_thread_cond_create(); 520 if (channel->cond_empty == NULL) { 521 status = ARES_ENOMEM; 522 goto done; 523 } 524 525 done: 526 if (status != ARES_SUCCESS) { 527 ares_channel_threading_destroy(channel); 528 } 529 return status; 530 } 531 532 void ares_channel_threading_destroy(ares_channel_t *channel) 533 { 534 ares_thread_mutex_destroy(channel->lock); 535 channel->lock = NULL; 536 ares_thread_cond_destroy(channel->cond_empty); 537 channel->cond_empty = NULL; 538 } 539 540 void ares_channel_lock(const ares_channel_t *channel) 541 { 542 ares_thread_mutex_lock(channel->lock); 543 } 544 545 void ares_channel_unlock(const ares_channel_t *channel) 546 { 547 ares_thread_mutex_unlock(channel->lock); 548 } 549 550 /* Must not be holding a channel lock already, public function only */ 551 ares_status_t ares_queue_wait_empty(ares_channel_t *channel, int timeout_ms) 552 { 553 ares_status_t status = ARES_SUCCESS; 554 ares_timeval_t tout; 555 556 if (!ares_threadsafety()) { 557 return ARES_ENOTIMP; 558 } 559 560 if (channel == NULL) { 561 return ARES_EFORMERR; 562 } 563 564 if (timeout_ms >= 0) { 565 ares_tvnow(&tout); 566 tout.sec += (ares_int64_t)(timeout_ms / 1000); 567 tout.usec += (unsigned int)(timeout_ms % 1000) * 1000; 568 } 569 570 ares_thread_mutex_lock(channel->lock); 571 while (ares_llist_len(channel->all_queries)) { 572 if (timeout_ms < 0) { 573 ares_thread_cond_wait(channel->cond_empty, channel->lock); 574 } else { 575 ares_timeval_t tv_remaining; 576 ares_timeval_t tv_now; 577 unsigned long tms; 578 579 ares_tvnow(&tv_now); 580 ares_timeval_remaining(&tv_remaining, &tv_now, &tout); 581 tms = 582 (unsigned long)((tv_remaining.sec * 1000) + (tv_remaining.usec / 1000)); 583 if (tms == 0) { 584 status = ARES_ETIMEOUT; 585 } else { 586 status = 587 ares_thread_cond_timedwait(channel->cond_empty, channel->lock, tms); 588 } 589 590 /* If there was a timeout, don't loop. Otherwise, make sure this wasn't 591 * a spurious wakeup by looping and checking the condition. */ 592 if (status == ARES_ETIMEOUT) { 593 break; 594 } 595 } 596 } 597 ares_thread_mutex_unlock(channel->lock); 598 return status; 599 } 600 601 void ares_queue_notify_empty(ares_channel_t *channel) 602 { 603 if (channel == NULL) { 604 return; 605 } 606 607 /* We are guaranteed to be holding a channel lock already */ 608 if (ares_llist_len(channel->all_queries)) { 609 return; 610 } 611 612 /* Notify all waiters of the conditional */ 613 ares_thread_cond_broadcast(channel->cond_empty); 614 }