• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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