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
ares_thread_mutex_create(void)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
ares_thread_mutex_destroy(ares_thread_mutex_t * mut)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
ares_thread_mutex_lock(ares_thread_mutex_t * mut)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
ares_thread_mutex_unlock(ares_thread_mutex_t * mut)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
ares_thread_cond_create(void)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
ares_thread_cond_destroy(ares_thread_cond_t * cond)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
ares_thread_cond_signal(ares_thread_cond_t * cond)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
ares_thread_cond_broadcast(ares_thread_cond_t * cond)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
ares_thread_cond_wait(ares_thread_cond_t * cond,ares_thread_mutex_t * mut)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
ares_thread_cond_timedwait(ares_thread_cond_t * cond,ares_thread_mutex_t * mut,unsigned long timeout_ms)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 */
ares_thread_func(LPVOID lpParameter)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
ares_thread_create(ares_thread_t ** thread,ares_thread_func_t func,void * arg)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
ares_thread_join(ares_thread_t * thread,void ** rv)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
ares_thread_mutex_create(void)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
ares_thread_mutex_destroy(ares_thread_mutex_t * mut)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
ares_thread_mutex_lock(ares_thread_mutex_t * mut)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
ares_thread_mutex_unlock(ares_thread_mutex_t * mut)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
ares_thread_cond_create(void)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
ares_thread_cond_destroy(ares_thread_cond_t * cond)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
ares_thread_cond_signal(ares_thread_cond_t * cond)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
ares_thread_cond_broadcast(ares_thread_cond_t * cond)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
ares_thread_cond_wait(ares_thread_cond_t * cond,ares_thread_mutex_t * mut)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
ares_timespec_timeout(struct timespec * ts,unsigned long add_ms)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
ares_thread_cond_timedwait(ares_thread_cond_t * cond,ares_thread_mutex_t * mut,unsigned long timeout_ms)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
ares_thread_create(ares_thread_t ** thread,ares_thread_func_t func,void * arg)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
ares_thread_join(ares_thread_t * thread,void ** rv)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
ares_threadsafety(void)415 ares_bool_t ares_threadsafety(void)
416 {
417 return ARES_TRUE;
418 }
419
420 #else /* !CARES_THREADS */
421
422 /* NoOp */
ares_thread_mutex_create(void)423 ares_thread_mutex_t *ares_thread_mutex_create(void)
424 {
425 return NULL;
426 }
427
ares_thread_mutex_destroy(ares_thread_mutex_t * mut)428 void ares_thread_mutex_destroy(ares_thread_mutex_t *mut)
429 {
430 (void)mut;
431 }
432
ares_thread_mutex_lock(ares_thread_mutex_t * mut)433 void ares_thread_mutex_lock(ares_thread_mutex_t *mut)
434 {
435 (void)mut;
436 }
437
ares_thread_mutex_unlock(ares_thread_mutex_t * mut)438 void ares_thread_mutex_unlock(ares_thread_mutex_t *mut)
439 {
440 (void)mut;
441 }
442
ares_thread_cond_create(void)443 ares_thread_cond_t *ares_thread_cond_create(void)
444 {
445 return NULL;
446 }
447
ares_thread_cond_destroy(ares_thread_cond_t * cond)448 void ares_thread_cond_destroy(ares_thread_cond_t *cond)
449 {
450 (void)cond;
451 }
452
ares_thread_cond_signal(ares_thread_cond_t * cond)453 void ares_thread_cond_signal(ares_thread_cond_t *cond)
454 {
455 (void)cond;
456 }
457
ares_thread_cond_broadcast(ares_thread_cond_t * cond)458 void ares_thread_cond_broadcast(ares_thread_cond_t *cond)
459 {
460 (void)cond;
461 }
462
ares_thread_cond_wait(ares_thread_cond_t * cond,ares_thread_mutex_t * mut)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
ares_thread_cond_timedwait(ares_thread_cond_t * cond,ares_thread_mutex_t * mut,unsigned long timeout_ms)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
ares_thread_create(ares_thread_t ** thread,ares_thread_func_t func,void * arg)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
ares_thread_join(ares_thread_t * thread,void ** rv)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
ares_threadsafety(void)497 ares_bool_t ares_threadsafety(void)
498 {
499 return ARES_FALSE;
500 }
501 #endif
502
503
ares_channel_threading_init(ares_channel_t * channel)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
ares_channel_threading_destroy(ares_channel_t * channel)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
ares_channel_lock(const ares_channel_t * channel)540 void ares_channel_lock(const ares_channel_t *channel)
541 {
542 ares_thread_mutex_lock(channel->lock);
543 }
544
ares_channel_unlock(const ares_channel_t * channel)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 */
ares_queue_wait_empty(ares_channel_t * channel,int timeout_ms)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
ares_queue_notify_empty(ares_channel_t * channel)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 }
615