quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

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 }