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_setup.h"
27#include "ares.h"
28#include "ares_private.h"
29
30#ifdef CARES_THREADS
31#  ifdef _WIN32
32
33struct ares__thread_mutex {
34  CRITICAL_SECTION mutex;
35};
36
37ares__thread_mutex_t *ares__thread_mutex_create(void)
38{
39  ares__thread_mutex_t *mut = ares_malloc_zero(sizeof(*mut));
40  if (mut == NULL) {
41    return NULL;
42  }
43
44  InitializeCriticalSection(&mut->mutex);
45  return mut;
46}
47
48void ares__thread_mutex_destroy(ares__thread_mutex_t *mut)
49{
50  if (mut == NULL) {
51    return;
52  }
53  DeleteCriticalSection(&mut->mutex);
54  ares_free(mut);
55}
56
57void ares__thread_mutex_lock(ares__thread_mutex_t *mut)
58{
59  if (mut == NULL) {
60    return;
61  }
62  EnterCriticalSection(&mut->mutex);
63}
64
65void ares__thread_mutex_unlock(ares__thread_mutex_t *mut)
66{
67  if (mut == NULL) {
68    return;
69  }
70  LeaveCriticalSection(&mut->mutex);
71}
72
73struct ares__thread_cond {
74  CONDITION_VARIABLE cond;
75};
76
77ares__thread_cond_t *ares__thread_cond_create(void)
78{
79  ares__thread_cond_t *cond = ares_malloc_zero(sizeof(*cond));
80  if (cond == NULL) {
81    return NULL;
82  }
83  InitializeConditionVariable(&cond->cond);
84  return cond;
85}
86
87void ares__thread_cond_destroy(ares__thread_cond_t *cond)
88{
89  if (cond == NULL) {
90    return;
91  }
92  ares_free(cond);
93}
94
95void ares__thread_cond_signal(ares__thread_cond_t *cond)
96{
97  if (cond == NULL) {
98    return;
99  }
100  WakeConditionVariable(&cond->cond);
101}
102
103void ares__thread_cond_broadcast(ares__thread_cond_t *cond)
104{
105  if (cond == NULL) {
106    return;
107  }
108  WakeAllConditionVariable(&cond->cond);
109}
110
111ares_status_t ares__thread_cond_wait(ares__thread_cond_t  *cond,
112                                     ares__thread_mutex_t *mut)
113{
114  if (cond == NULL || mut == NULL) {
115    return ARES_EFORMERR;
116  }
117
118  SleepConditionVariableCS(&cond->cond, &mut->mutex, INFINITE);
119  return ARES_SUCCESS;
120}
121
122ares_status_t ares__thread_cond_timedwait(ares__thread_cond_t  *cond,
123                                          ares__thread_mutex_t *mut,
124                                          unsigned long         timeout_ms)
125{
126  if (cond == NULL || mut == NULL) {
127    return ARES_EFORMERR;
128  }
129
130  if (!SleepConditionVariableCS(&cond->cond, &mut->mutex, timeout_ms)) {
131    return ARES_ETIMEOUT;
132  }
133
134  return ARES_SUCCESS;
135}
136
137struct ares__thread {
138  HANDLE thread;
139  DWORD  id;
140
141  void  *(*func)(void *arg);
142  void  *arg;
143  void  *rv;
144};
145
146/* Wrap for pthread compatibility */
147static DWORD WINAPI ares__thread_func(LPVOID lpParameter)
148{
149  ares__thread_t *thread = lpParameter;
150
151  thread->rv = thread->func(thread->arg);
152  return 0;
153}
154
155ares_status_t ares__thread_create(ares__thread_t    **thread,
156                                  ares__thread_func_t func, void *arg)
157{
158  ares__thread_t *thr = NULL;
159
160  if (func == NULL || thread == NULL) {
161    return ARES_EFORMERR;
162  }
163
164  thr = ares_malloc_zero(sizeof(*thr));
165  if (thr == NULL) {
166    return ARES_ENOMEM;
167  }
168
169  thr->func   = func;
170  thr->arg    = arg;
171  thr->thread = CreateThread(NULL, 0, ares__thread_func, thr, 0, &thr->id);
172  if (thr->thread == NULL) {
173    ares_free(thr);
174    return ARES_ESERVFAIL;
175  }
176
177  *thread = thr;
178  return ARES_SUCCESS;
179}
180
181ares_status_t ares__thread_join(ares__thread_t *thread, void **rv)
182{
183  ares_status_t status = ARES_SUCCESS;
184
185  if (thread == NULL) {
186    return ARES_EFORMERR;
187  }
188
189  if (WaitForSingleObject(thread->thread, INFINITE) != WAIT_OBJECT_0) {
190    status = ARES_ENOTFOUND;
191  } else {
192    CloseHandle(thread->thread);
193  }
194
195  if (status == ARES_SUCCESS && rv != NULL) {
196    *rv = thread->rv;
197  }
198  ares_free(thread);
199
200  return status;
201}
202
203#  else /* !WIN32 == PTHREAD */
204#    include <pthread.h>
205
206/* for clock_gettime() */
207#    ifdef HAVE_TIME_H
208#      include <time.h>
209#    endif
210
211/* for gettimeofday() */
212#    ifdef HAVE_SYS_TIME_H
213#      include <sys/time.h>
214#    endif
215
216struct ares__thread_mutex {
217  pthread_mutex_t mutex;
218};
219
220ares__thread_mutex_t *ares__thread_mutex_create(void)
221{
222  pthread_mutexattr_t   attr;
223  ares__thread_mutex_t *mut = ares_malloc_zero(sizeof(*mut));
224  if (mut == NULL) {
225    return NULL;
226  }
227
228  if (pthread_mutexattr_init(&attr) != 0) {
229    ares_free(mut);
230    return NULL;
231  }
232
233  if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) {
234    goto fail;
235  }
236
237  if (pthread_mutex_init(&mut->mutex, &attr) != 0) {
238    goto fail;
239  }
240
241  pthread_mutexattr_destroy(&attr);
242  return mut;
243
244fail:
245  pthread_mutexattr_destroy(&attr);
246  ares_free(mut);
247  return NULL;
248}
249
250void 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
259void 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
267void 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
275struct ares__thread_cond {
276  pthread_cond_t cond;
277};
278
279ares__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
289void 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
298void 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
306void 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
314ares_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
325static 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  += add_ms / 1000;
339  ts->tv_nsec += (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
348ares_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
367struct ares__thread {
368  pthread_t thread;
369};
370
371ares_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;
383  }
384  if (pthread_create(&thr->thread, NULL, func, arg) != 0) {
385    ares_free(thr);
386    return ARES_ESERVFAIL;
387  }
388
389  *thread = thr;
390  return ARES_SUCCESS;
391}
392
393ares_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
415ares_bool_t ares_threadsafety(void)
416{
417  return ARES_TRUE;
418}
419
420#else /* !CARES_THREADS */
421
422/* NoOp */
423ares__thread_mutex_t *ares__thread_mutex_create(void)
424{
425  return NULL;
426}
427
428void ares__thread_mutex_destroy(ares__thread_mutex_t *mut)
429{
430  (void)mut;
431}
432
433void ares__thread_mutex_lock(ares__thread_mutex_t *mut)
434{
435  (void)mut;
436}
437
438void ares__thread_mutex_unlock(ares__thread_mutex_t *mut)
439{
440  (void)mut;
441}
442
443ares__thread_cond_t *ares__thread_cond_create(void)
444{
445  return NULL;
446}
447
448void ares__thread_cond_destroy(ares__thread_cond_t *cond)
449{
450  (void)cond;
451}
452
453void ares__thread_cond_signal(ares__thread_cond_t *cond)
454{
455  (void)cond;
456}
457
458void ares__thread_cond_broadcast(ares__thread_cond_t *cond)
459{
460  (void)cond;
461}
462
463ares_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
471ares_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
481ares_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
490ares_status_t ares__thread_join(ares__thread_t *thread, void **rv)
491{
492  (void)thread;
493  (void)rv;
494  return ARES_ENOTIMP;
495}
496
497ares_bool_t ares_threadsafety(void)
498{
499  return ARES_FALSE;
500}
501#endif
502
503
504ares_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
525done:
526  if (status != ARES_SUCCESS) {
527    ares__channel_threading_destroy(channel);
528  }
529  return status;
530}
531
532void 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
540void ares__channel_lock(ares_channel_t *channel)
541{
542  ares__thread_mutex_lock(channel->lock);
543}
544
545void ares__channel_unlock(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 */
551ares_status_t ares_queue_wait_empty(ares_channel_t *channel, int timeout_ms)
552{
553  ares_status_t  status = ARES_SUCCESS;
554  struct timeval 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    tout          = ares__tvnow();
566    tout.tv_sec  += timeout_ms / 1000;
567    tout.tv_usec += (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      struct timeval tv_remaining;
576      struct timeval tv_now = ares__tvnow();
577      unsigned long  tms;
578
579      ares__timeval_remaining(&tv_remaining, &tv_now, &tout);
580      tms = (unsigned long)((tv_remaining.tv_sec * 1000) +
581                            (tv_remaining.tv_usec / 1000));
582      if (tms == 0) {
583        status = ARES_ETIMEOUT;
584      } else {
585        status =
586          ares__thread_cond_timedwait(channel->cond_empty, channel->lock, tms);
587      }
588    }
589  }
590  ares__thread_mutex_unlock(channel->lock);
591  return status;
592}
593
594void ares_queue_notify_empty(ares_channel_t *channel)
595{
596  if (channel == NULL) {
597    return;
598  }
599
600  /* We are guaranteed to be holding a channel lock already */
601  if (ares__llist_len(channel->all_queries)) {
602    return;
603  }
604
605  /* Notify all waiters of the conditional */
606  ares__thread_cond_broadcast(channel->cond_empty);
607}
608