• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

c-ares / c-ares / 12750601927

13 Jan 2025 03:33PM UTC coverage: 91.498%. Remained the same
12750601927

Pull #960

github

web-flow
Merge a1047b166 into 0518d404e
Pull Request #960: [port] Windows XP: try to support threading and event subsystems

0 of 3 new or added lines in 1 file covered. (0.0%)

2 existing lines in 2 files now uncovered.

22407 of 24489 relevant lines covered (91.5%)

11897.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.27
/src/lib/util/ares_threads.c
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
#ifdef HAVE_STDINT_H
28
#  include <stdint.h>
29
#endif
30

31
#ifdef CARES_THREADS
32
#  ifdef _WIN32
33

34
struct ares_thread_mutex {
35
  CRITICAL_SECTION mutex;
36
};
37

38
ares_thread_mutex_t *ares_thread_mutex_create(void)
39
{
40
  ares_thread_mutex_t *mut = ares_malloc_zero(sizeof(*mut));
41
  if (mut == NULL) {
42
    return NULL;
43
  }
44

45
  InitializeCriticalSection(&mut->mutex);
46
  return mut;
47
}
48

49
void ares_thread_mutex_destroy(ares_thread_mutex_t *mut)
50
{
51
  if (mut == NULL) {
52
    return;
53
  }
54
  DeleteCriticalSection(&mut->mutex);
55
  ares_free(mut);
56
}
57

58
void ares_thread_mutex_lock(ares_thread_mutex_t *mut)
59
{
60
  if (mut == NULL) {
61
    return;
62
  }
63
  EnterCriticalSection(&mut->mutex);
64
}
65

66
void ares_thread_mutex_unlock(ares_thread_mutex_t *mut)
67
{
68
  if (mut == NULL) {
69
    return;
70
  }
71
  LeaveCriticalSection(&mut->mutex);
72
}
73

74
#    if _WIN32_WINNT >= 0x0600 /* Vista */
75

76
struct ares_thread_cond {
77
  CONDITION_VARIABLE cond;
78
};
79

80
ares_thread_cond_t *ares_thread_cond_create(void)
81
{
82
  ares_thread_cond_t *cond = ares_malloc_zero(sizeof(*cond));
83
  if (cond == NULL) {
84
    return NULL;
85
  }
86
  InitializeConditionVariable(&cond->cond);
87
  return cond;
88
}
89

90
void ares_thread_cond_destroy(ares_thread_cond_t *cond)
91
{
92
  if (cond == NULL) {
93
    return;
94
  }
95
  ares_free(cond);
96
}
97

98
void ares_thread_cond_signal(ares_thread_cond_t *cond)
99
{
100
  if (cond == NULL) {
101
    return;
102
  }
103
  WakeConditionVariable(&cond->cond);
104
}
105

106
void ares_thread_cond_broadcast(ares_thread_cond_t *cond)
107
{
108
  if (cond == NULL) {
109
    return;
110
  }
111
  WakeAllConditionVariable(&cond->cond);
112
}
113

114
ares_status_t ares_thread_cond_wait(ares_thread_cond_t  *cond,
115
                                    ares_thread_mutex_t *mut)
116
{
117
  if (cond == NULL || mut == NULL) {
118
    return ARES_EFORMERR;
119
  }
120

121
  SleepConditionVariableCS(&cond->cond, &mut->mutex, INFINITE);
122
  return ARES_SUCCESS;
123
}
124

125
ares_status_t ares_thread_cond_timedwait(ares_thread_cond_t  *cond,
126
                                         ares_thread_mutex_t *mut,
127
                                         size_t               timeout_ms)
128
{
129
  DWORD tout;
130

131
  if (cond == NULL || mut == NULL) {
132
    return ARES_EFORMERR;
133
  }
134

135
  if (timeout_ms == SIZE_MAX) {
136
    tout = INFINITE;
137
  } else {
138
    tout = (DWORD)timeout_ms;
139
  }
140

141
  if (!SleepConditionVariableCS(&cond->cond, &mut->mutex, tout)) {
142
    return ARES_ETIMEOUT;
143
  }
144

145
  return ARES_SUCCESS;
146
}
147

148
#    else
149

150
typedef enum {
151
  ARES_W32_COND_SIGNAL    = 0,
152
  ARES_W32_COND_BROADCAST,
153
  ARES_W32_COND_EVMAX,
154
  ARES_W32_COND_NONE = ARES_W32_COND_EVMAX
155
} ares_w32_cond_event_t;
156

157
struct ares_thread_cond {
158
  HANDLE                events[2];
159
  HANDLE                gate;
160
  CRITICAL_SECTION      mutex;
161
  size_t                waiters;
162
  ares_w32_cond_event_t event;
163
};
164

165
ares_thread_cond_t *ares_thread_cond_create(void)
166
{
167
  ares_thread_cond_t *cond;
168

169
  cond = ares_malloc_zero(sizeof(*cond));
170
  if (cond == NULL) {
171
    return NULL;
172
  }
173

174
  cond->events[ARES_W32_COND_SIGNAL] = CreateEvent(NULL, FALSE, FALSE, NULL);
175
  if (cond->events[ARES_W32_COND_SIGNAL] == NULL) {
176
    goto fail;
177
  }
178

179
  cond->events[ARES_W32_COND_BROADCAST] = CreateEvent(NULL, TRUE, FALSE, NULL);
180
  if (cond->events[ARES_W32_COND_BROADCAST] == NULL) {
181
    goto fail;
182
  }
183

184
  /* Use a semaphore as a gate so we don't lose signals */
185
  cond->gate = CreateSemaphore(NULL, 1, 1, NULL);
186
  if (cond->gate == NULL) {
187
    goto fail;
188
  }
189

190
  InitializeCriticalSection(&cond->mutex);
191
  cond->waiters = 0;
192
  cond->event   = ARES_W32_COND_NONE;
193

194
  return cond;
195

196
fail:
197
  ares_thread_cond_destroy(cond);
198
  return NULL;
199
}
200

201
void ares_thread_cond_destroy(ares_thread_cond_t *cond)
202
{
203
  if (cond == NULL)
204
    return;
205

206
  if (cond->events[ARES_W32_COND_SIGNAL]) {
207
    CloseHandle(cond->events[ARES_W32_COND_SIGNAL]);
208
  }
209
  if (cond->events[ARES_W32_COND_BROADCAST]) {
210
    CloseHandle(cond->events[ARES_W32_COND_BROADCAST]);
211
  }
212
  if (cond->gate) {
213
    CloseHandle(cond->gate);
214
  }
215
  DeleteCriticalSection(&cond->mutex);
216

217
  ares_free(cond);
218
}
219

220
ares_status_t ares_thread_cond_timedwait(ares_thread_cond_t  *cond,
221
                                         ares_thread_mutex_t *mut,
222
                                         size_t               timeout_ms)
223
{
224
  DWORD rv;
225
  DWORD dwMilliseconds;
226

227
  if (cond == NULL || mut == NULL) {
228
    return ARES_EFORMERR;
229
  }
230

231
  /* We may only enter when no wakeups active this will prevent the lost
232
   * wakeup */
233
  WaitForSingleObject(cond->gate, INFINITE);
234

235
  EnterCriticalSection(&cond->mutex);
236
  /* count waiters passing through */
237
  cond->waiters++;
238
  LeaveCriticalSection(&cond->mutex);
239

240
  /* Open Gate */
241
  ReleaseSemaphore(cond->gate, 1, NULL);
242

243
  /* Release passed in mutex */
244
  ares_thread_mutex_unlock(mut);
245

246
  if (timeout_ms == SIZE_MAX) {
247
    dwMilliseconds = INFINITE;
248
  } else {
249
    dwMilliseconds = (DWORD)timeout_ms;
250
  }
251
  rv = WaitForMultipleObjects(ARES_W32_COND_EVMAX, cond->events, FALSE,
252
                              dwMilliseconds);
253

254
  /* We go into a critical section to make sure cond->waiters isn't checked
255
   * while we decrement.  This is especially important for a timeout since the
256
   * gate may not be closed. We need to check to see if a broadcast/signal was
257
   * pending as this thread could have been preempted prior to
258
   * EnterCriticalSection but after WaitForMultipleObjects() so we may be
259
   * responsible for resetting the event and closing the gate */
260
  EnterCriticalSection(&cond->mutex);
261
  cond->waiters--;
262

263
  if (cond->event != ARES_W32_COND_NONE && cond->waiters == 0) {
264
    /* Last waiter needs to reset the event on(as a broadcast event is not
265
     * automatic) and also re-open the gate */
266
    if (cond->event == ARES_W32_COND_BROADCAST) {
267
      ResetEvent(cond->events[ARES_W32_COND_BROADCAST]);
268
    }
269

270
    /* Open Gate (closed by ares_thread_cond_broadcast()) since there are no
271
     * more waiters for the event */
272
    ReleaseSemaphore(cond->gate, 1, NULL);
273
    cond->event = ARES_W32_COND_NONE;
274
  } else if (rv == WAIT_OBJECT_0 + ARES_W32_COND_SIGNAL) {
275
    /* If specifically, this thread was signalled and there are more waiting,
276
     * re-open the gate and reset the event */
277
    ReleaseSemaphore(cond->gate, 1, NULL);
278
    cond->event = ARES_W32_COND_NONE;
279
  } else {
280
    /* This could be a standard timeout with more waiters, don't do anything */
281
  }
282
  LeaveCriticalSection(&cond->mutex);
283

284
  /* re-lock the passed in mutex */
285
  ares_thread_mutex_lock(mut);
286

287
  if (rv == WAIT_TIMEOUT) {
288
    return ARES_ETIMEOUT;
289
  }
290

291
  return ARES_SUCCESS;
292
}
293

294
ares_status_t ares_thread_cond_wait(ares_thread_cond_t  *cond,
295
                                    ares_thread_mutex_t *mut)
296
{
297
  return ares_thread_cond_timedwait(cond, mut, SIZE_MAX);
298
}
299

300
void ares_thread_cond_broadcast(ares_thread_cond_t *cond)
301
{
302
  if (cond == NULL) {
303
    return;
304
  }
305

306
  /* close gate to prevent more waiters while broadcasting */
307
  WaitForSingleObject(cond->gate, INFINITE);
308

309
  /* If there are waiters, send a broadcast event,
310
   * otherwise, just reopen the gate */
311
  EnterCriticalSection(&cond->mutex);
312

313
  cond->event = ARES_W32_COND_BROADCAST;
314
  if (cond->waiters) {
315
    /* wake all waiters */
316
    SetEvent(cond->events[ARES_W32_COND_BROADCAST]);
317
  } else {
318
    /* if no waiters just reopen gate */
319
    ReleaseSemaphore(cond->gate, 1, NULL);
320
  }
321

322
  LeaveCriticalSection(&cond->mutex);
323
}
324

325
void ares_thread_cond_signal(ares_thread_cond_t *cond)
326
{
327
  if (cond == NULL) {
328
    return;
329
  }
330

331
  /* close gate to prevent more waiters while signalling */
332
  WaitForSingleObject(cond->gate, INFINITE);
333

334
  EnterCriticalSection(&cond->mutex);
335
  cond->event = ARES_W32_COND_SIGNAL;
336
  if (cond->waiters) {
337
    /* wake one waiter */
338
    SetEvent(cond->events[ARES_W32_COND_SIGNAL]);
339
  } else {
340
    /* no waiters, just reopen the gate */
341
    ReleaseSemaphore(cond->gate, 1, NULL);
342
  }
343
  LeaveCriticalSection(&cond->mutex);
344
}
345

346
#    endif
347

348
struct ares_thread {
349
  HANDLE thread;
350
  DWORD  id;
351

352
  void *(*func)(void *arg);
353
  void *arg;
354
  void *rv;
355
};
356

357
/* Wrap for pthread compatibility */
358
static DWORD WINAPI ares_thread_func(LPVOID lpParameter)
359
{
360
  ares_thread_t *thread = lpParameter;
361

362
  thread->rv = thread->func(thread->arg);
363
  return 0;
364
}
365

366
ares_status_t ares_thread_create(ares_thread_t    **thread,
367
                                 ares_thread_func_t func, void *arg)
368
{
369
  ares_thread_t *thr = NULL;
370

371
  if (func == NULL || thread == NULL) {
372
    return ARES_EFORMERR;
373
  }
374

375
  thr = ares_malloc_zero(sizeof(*thr));
376
  if (thr == NULL) {
377
    return ARES_ENOMEM;
378
  }
379

380
  thr->func   = func;
381
  thr->arg    = arg;
382
  thr->thread = CreateThread(NULL, 0, ares_thread_func, thr, 0, &thr->id);
383
  if (thr->thread == NULL) {
384
    ares_free(thr);
385
    return ARES_ESERVFAIL;
386
  }
387

388
  *thread = thr;
389
  return ARES_SUCCESS;
390
}
391

392
ares_status_t ares_thread_join(ares_thread_t *thread, void **rv)
393
{
394
  ares_status_t status = ARES_SUCCESS;
395

396
  if (thread == NULL) {
397
    return ARES_EFORMERR;
398
  }
399

400
  if (WaitForSingleObject(thread->thread, INFINITE) != WAIT_OBJECT_0) {
401
    status = ARES_ENOTFOUND;
402
  } else {
403
    CloseHandle(thread->thread);
404
  }
405

406
  if (status == ARES_SUCCESS && rv != NULL) {
407
    *rv = thread->rv;
408
  }
409
  ares_free(thread);
410

411
  return status;
412
}
413

414
#  else /* !WIN32 == PTHREAD */
415
#    include <pthread.h>
416

417
/* for clock_gettime() */
418
#    ifdef HAVE_TIME_H
419
#      include <time.h>
420
#    endif
421

422
/* for gettimeofday() */
423
#    ifdef HAVE_SYS_TIME_H
424
#      include <sys/time.h>
425
#    endif
426

427
struct ares_thread_mutex {
428
  pthread_mutex_t mutex;
429
};
430

431
ares_thread_mutex_t *ares_thread_mutex_create(void)
1,634✔
432
{
433
  pthread_mutexattr_t  attr;
434
  ares_thread_mutex_t *mut = ares_malloc_zero(sizeof(*mut));
1,634✔
435
  if (mut == NULL) {
1,634✔
436
    return NULL;
4✔
437
  }
438

439
  if (pthread_mutexattr_init(&attr) != 0) {
1,630✔
440
    ares_free(mut); /* LCOV_EXCL_LINE: UntestablePath */
441
    return NULL;    /* LCOV_EXCL_LINE: UntestablePath */
442
  }
443

444
  if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) {
1,630✔
445
    goto fail; /* LCOV_EXCL_LINE: UntestablePath */
446
  }
447

448
  if (pthread_mutex_init(&mut->mutex, &attr) != 0) {
1,630✔
449
    goto fail; /* LCOV_EXCL_LINE: UntestablePath */
450
  }
451

452
  pthread_mutexattr_destroy(&attr);
1,630✔
453
  return mut;
1,630✔
454

455
/* LCOV_EXCL_START: UntestablePath */
456
fail:
457
  pthread_mutexattr_destroy(&attr);
458
  ares_free(mut);
459
  return NULL;
460
  /* LCOV_EXCL_STOP */
461
}
462

463
void ares_thread_mutex_destroy(ares_thread_mutex_t *mut)
1,641✔
464
{
465
  if (mut == NULL) {
1,641✔
466
    return;
12✔
467
  }
468
  pthread_mutex_destroy(&mut->mutex);
1,629✔
469
  ares_free(mut);
1,629✔
470
}
471

472
void ares_thread_mutex_lock(ares_thread_mutex_t *mut)
93,744✔
473
{
474
  if (mut == NULL) {
93,744✔
475
    return;
16✔
476
  }
477
  pthread_mutex_lock(&mut->mutex);
93,728✔
478
}
479

480
void ares_thread_mutex_unlock(ares_thread_mutex_t *mut)
93,746✔
481
{
482
  if (mut == NULL) {
93,746✔
483
    return;
16✔
484
  }
485
  pthread_mutex_unlock(&mut->mutex);
93,730✔
486
}
487

488
struct ares_thread_cond {
489
  pthread_cond_t cond;
490
};
491

492
ares_thread_cond_t *ares_thread_cond_create(void)
1,108✔
493
{
494
  ares_thread_cond_t *cond = ares_malloc_zero(sizeof(*cond));
1,108✔
495
  if (cond == NULL) {
1,108✔
496
    return NULL;
4✔
497
  }
498
  pthread_cond_init(&cond->cond, NULL);
1,104✔
499
  return cond;
1,104✔
500
}
501

502
void ares_thread_cond_destroy(ares_thread_cond_t *cond)
1,119✔
503
{
504
  if (cond == NULL) {
1,119✔
505
    return;
16✔
506
  }
507
  pthread_cond_destroy(&cond->cond);
1,103✔
508
  ares_free(cond);
1,103✔
509
}
510

511
void ares_thread_cond_signal(ares_thread_cond_t *cond)
×
512
{
513
  if (cond == NULL) {
×
514
    return;
×
515
  }
516
  pthread_cond_signal(&cond->cond);
×
517
}
518

519
void ares_thread_cond_broadcast(ares_thread_cond_t *cond)
2,317✔
520
{
521
  if (cond == NULL) {
2,317✔
522
    return;
8✔
523
  }
524
  pthread_cond_broadcast(&cond->cond);
2,309✔
525
}
526

527
ares_status_t ares_thread_cond_wait(ares_thread_cond_t  *cond,
×
528
                                    ares_thread_mutex_t *mut)
529
{
530
  if (cond == NULL || mut == NULL) {
×
531
    return ARES_EFORMERR;
×
532
  }
533

534
  pthread_cond_wait(&cond->cond, &mut->mutex);
×
535
  return ARES_SUCCESS;
×
536
}
537

NEW
538
static void ares_timespec_timeout(struct timespec *ts, size_t add_ms)
×
539
{
540
#    if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_REALTIME)
541
  clock_gettime(CLOCK_REALTIME, ts);
542
#    elif defined(HAVE_GETTIMEOFDAY)
543
  struct timeval tv;
544
  gettimeofday(&tv, NULL);
×
545
  ts->tv_sec  = tv.tv_sec;
×
546
  ts->tv_nsec = tv.tv_usec * 1000;
×
547
#    else
548
#      error cannot determine current system time
549
#    endif
550

551
  ts->tv_sec  += (time_t)(add_ms / 1000);
×
552
  ts->tv_nsec += (long)((add_ms % 1000) * 1000000);
×
553

554
  /* Normalize if needed */
555
  if (ts->tv_nsec >= 1000000000) {
×
556
    ts->tv_sec  += ts->tv_nsec / 1000000000;
×
557
    ts->tv_nsec %= 1000000000;
×
558
  }
559
}
×
560

561
ares_status_t ares_thread_cond_timedwait(ares_thread_cond_t  *cond,
×
562
                                         ares_thread_mutex_t *mut,
563
                                         size_t               timeout_ms)
564
{
565
  struct timespec ts;
566

567
  if (cond == NULL || mut == NULL) {
×
568
    return ARES_EFORMERR;
×
569
  }
570

NEW
571
  if (timeout_ms == SIZE_MAX) {
×
NEW
572
    return ares_thread_cond_wait(cond, mut);
×
573
  }
574

UNCOV
575
  ares_timespec_timeout(&ts, timeout_ms);
×
576

577
  if (pthread_cond_timedwait(&cond->cond, &mut->mutex, &ts) != 0) {
×
578
    return ARES_ETIMEOUT;
×
579
  }
580

581
  return ARES_SUCCESS;
×
582
}
583

584
struct ares_thread {
585
  pthread_t thread;
586
};
587

588
ares_status_t ares_thread_create(ares_thread_t    **thread,
582✔
589
                                 ares_thread_func_t func, void *arg)
590
{
591
  ares_thread_t *thr = NULL;
582✔
592

593
  if (func == NULL || thread == NULL) {
582✔
594
    return ARES_EFORMERR;
×
595
  }
596

597
  thr = ares_malloc_zero(sizeof(*thr));
582✔
598
  if (thr == NULL) {
582✔
599
    return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
600
  }
601
  if (pthread_create(&thr->thread, NULL, func, arg) != 0) {
582✔
602
    ares_free(thr);        /* LCOV_EXCL_LINE: UntestablePath */
603
    return ARES_ESERVFAIL; /* LCOV_EXCL_LINE: UntestablePath */
604
  }
605

606
  *thread = thr;
582✔
607
  return ARES_SUCCESS;
582✔
608
}
609

610
ares_status_t ares_thread_join(ares_thread_t *thread, void **rv)
582✔
611
{
612
  void         *ret    = NULL;
582✔
613
  ares_status_t status = ARES_SUCCESS;
582✔
614

615
  if (thread == NULL) {
582✔
616
    return ARES_EFORMERR;
×
617
  }
618

619
  if (pthread_join(thread->thread, &ret) != 0) {
582✔
620
    status = ARES_ENOTFOUND;
×
621
  }
622
  ares_free(thread);
582✔
623

624
  if (status == ARES_SUCCESS && rv != NULL) {
582✔
625
    *rv = ret;
582✔
626
  }
627
  return status;
582✔
628
}
629

630
#  endif
631

632
ares_bool_t ares_threadsafety(void)
1,694✔
633
{
634
  return ARES_TRUE;
1,694✔
635
}
636

637
#else /* !CARES_THREADS */
638

639
/* NoOp */
640
ares_thread_mutex_t *ares_thread_mutex_create(void)
641
{
642
  return NULL;
643
}
644

645
void ares_thread_mutex_destroy(ares_thread_mutex_t *mut)
646
{
647
  (void)mut;
648
}
649

650
void ares_thread_mutex_lock(ares_thread_mutex_t *mut)
651
{
652
  (void)mut;
653
}
654

655
void ares_thread_mutex_unlock(ares_thread_mutex_t *mut)
656
{
657
  (void)mut;
658
}
659

660
ares_thread_cond_t *ares_thread_cond_create(void)
661
{
662
  return NULL;
663
}
664

665
void ares_thread_cond_destroy(ares_thread_cond_t *cond)
666
{
667
  (void)cond;
668
}
669

670
void ares_thread_cond_signal(ares_thread_cond_t *cond)
671
{
672
  (void)cond;
673
}
674

675
void ares_thread_cond_broadcast(ares_thread_cond_t *cond)
676
{
677
  (void)cond;
678
}
679

680
ares_status_t ares_thread_cond_wait(ares_thread_cond_t  *cond,
681
                                    ares_thread_mutex_t *mut)
682
{
683
  (void)cond;
684
  (void)mut;
685
  return ARES_ENOTIMP;
686
}
687

688
ares_status_t ares_thread_cond_timedwait(ares_thread_cond_t  *cond,
689
                                         ares_thread_mutex_t *mut,
690
                                         size_t               timeout_ms)
691
{
692
  (void)cond;
693
  (void)mut;
694
  (void)timeout_ms;
695
  return ARES_ENOTIMP;
696
}
697

698
ares_status_t ares_thread_create(ares_thread_t    **thread,
699
                                 ares_thread_func_t func, void *arg)
700
{
701
  (void)thread;
702
  (void)func;
703
  (void)arg;
704
  return ARES_ENOTIMP;
705
}
706

707
ares_status_t ares_thread_join(ares_thread_t *thread, void **rv)
708
{
709
  (void)thread;
710
  (void)rv;
711
  return ARES_ENOTIMP;
712
}
713

714
ares_bool_t ares_threadsafety(void)
715
{
716
  return ARES_FALSE;
717
}
718
#endif
719

720

721
ares_status_t ares_channel_threading_init(ares_channel_t *channel)
1,112✔
722
{
723
  ares_status_t status = ARES_SUCCESS;
1,112✔
724

725
  /* Threading is optional! */
726
  if (!ares_threadsafety()) {
1,112✔
727
    return ARES_SUCCESS;
×
728
  }
729

730
  channel->lock = ares_thread_mutex_create();
1,112✔
731
  if (channel->lock == NULL) {
1,112✔
732
    status = ARES_ENOMEM;
4✔
733
    goto done;
4✔
734
  }
735

736
  channel->cond_empty = ares_thread_cond_create();
1,108✔
737
  if (channel->cond_empty == NULL) {
1,108✔
738
    status = ARES_ENOMEM;
4✔
739
    goto done;
4✔
740
  }
741

742
done:
1,104✔
743
  if (status != ARES_SUCCESS) {
1,112✔
744
    ares_channel_threading_destroy(channel);
8✔
745
  }
746
  return status;
1,112✔
747
}
748

749
void ares_channel_threading_destroy(ares_channel_t *channel)
1,119✔
750
{
751
  ares_thread_mutex_destroy(channel->lock);
1,119✔
752
  channel->lock = NULL;
1,119✔
753
  ares_thread_cond_destroy(channel->cond_empty);
1,119✔
754
  channel->cond_empty = NULL;
1,119✔
755
}
1,119✔
756

757
void ares_channel_lock(const ares_channel_t *channel)
73,876✔
758
{
759
  ares_thread_mutex_lock(channel->lock);
73,876✔
760
}
73,876✔
761

762
void ares_channel_unlock(const ares_channel_t *channel)
73,876✔
763
{
764
  ares_thread_mutex_unlock(channel->lock);
73,876✔
765
}
73,876✔
766

767
/* Must not be holding a channel lock already, public function only */
768
ares_status_t ares_queue_wait_empty(ares_channel_t *channel, int timeout_ms)
×
769
{
770
  ares_status_t  status = ARES_SUCCESS;
×
771
  ares_timeval_t tout;
772

773
  if (!ares_threadsafety()) {
×
774
    return ARES_ENOTIMP;
×
775
  }
776

777
  if (channel == NULL) {
×
778
    return ARES_EFORMERR;
×
779
  }
780

781
  if (timeout_ms >= 0) {
×
782
    ares_tvnow(&tout);
×
783
    tout.sec  += (ares_int64_t)(timeout_ms / 1000);
×
784
    tout.usec += (unsigned int)(timeout_ms % 1000) * 1000;
×
785
  }
786

787
  ares_thread_mutex_lock(channel->lock);
×
788
  while (ares_llist_len(channel->all_queries)) {
×
789
    if (timeout_ms < 0) {
×
790
      ares_thread_cond_wait(channel->cond_empty, channel->lock);
×
791
    } else {
792
      ares_timeval_t tv_remaining;
793
      ares_timeval_t tv_now;
794
      unsigned long  tms;
795

796
      ares_tvnow(&tv_now);
×
797
      ares_timeval_remaining(&tv_remaining, &tv_now, &tout);
×
798
      tms =
×
799
        (unsigned long)((tv_remaining.sec * 1000) + (tv_remaining.usec / 1000));
×
800
      if (tms == 0) {
×
801
        status = ARES_ETIMEOUT;
×
802
      } else {
803
        status =
804
          ares_thread_cond_timedwait(channel->cond_empty, channel->lock, tms);
×
805
      }
806

807
      /* If there was a timeout, don't loop.  Otherwise, make sure this wasn't
808
       * a spurious wakeup by looping and checking the condition. */
809
      if (status == ARES_ETIMEOUT) {
×
810
        break;
×
811
      }
812
    }
813
  }
814
  ares_thread_mutex_unlock(channel->lock);
×
815
  return status;
×
816
}
817

818
void ares_queue_notify_empty(ares_channel_t *channel)
3,437✔
819
{
820
  if (channel == NULL) {
3,437✔
821
    return;
×
822
  }
823

824
  /* We are guaranteed to be holding a channel lock already */
825
  if (ares_llist_len(channel->all_queries)) {
3,437✔
826
    return;
1,120✔
827
  }
828

829
  /* Notify all waiters of the conditional */
830
  ares_thread_cond_broadcast(channel->cond_empty);
2,317✔
831
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc