tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

aom_thread.c (8486B)


      1 /*
      2 * Copyright (c) 2016, Alliance for Open Media. All rights reserved.
      3 *
      4 * This source code is subject to the terms of the BSD 2 Clause License and
      5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
      6 * was not distributed with this source code in the LICENSE file, you can
      7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
      8 * Media Patent License 1.0 was not distributed with this source code in the
      9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
     10 */
     11 //
     12 // Multi-threaded worker
     13 //
     14 // Original source:
     15 //  https://chromium.googlesource.com/webm/libwebp
     16 
     17 // Enable GNU extensions in glibc so that we can call pthread_setname_np().
     18 // This must be before any #include statements.
     19 #ifndef _GNU_SOURCE
     20 #define _GNU_SOURCE
     21 #endif
     22 
     23 #include <assert.h>
     24 #include <string.h>  // for memset()
     25 
     26 #include "config/aom_config.h"
     27 
     28 #include "aom_mem/aom_mem.h"
     29 #include "aom_ports/sanitizer.h"
     30 #include "aom_util/aom_pthread.h"
     31 #include "aom_util/aom_thread.h"
     32 
     33 #if CONFIG_MULTITHREAD
     34 
     35 struct AVxWorkerImpl {
     36  pthread_mutex_t mutex_;
     37  pthread_cond_t condition_;
     38  pthread_t thread_;
     39 };
     40 
     41 //------------------------------------------------------------------------------
     42 
     43 static void execute(AVxWorker *const worker);  // Forward declaration.
     44 
     45 static THREADFN thread_loop(void *ptr) {
     46  AVxWorker *const worker = (AVxWorker *)ptr;
     47 #ifdef __APPLE__
     48  if (worker->thread_name != NULL) {
     49    // Apple's version of pthread_setname_np takes one argument and operates on
     50    // the current thread only. The maximum size of the thread_name buffer was
     51    // noted in the Chromium source code and was confirmed by experiments. If
     52    // thread_name is too long, pthread_setname_np returns -1 with errno
     53    // ENAMETOOLONG (63).
     54    char thread_name[64];
     55    strncpy(thread_name, worker->thread_name, sizeof(thread_name) - 1);
     56    thread_name[sizeof(thread_name) - 1] = '\0';
     57    pthread_setname_np(thread_name);
     58  }
     59 #elif (defined(__GLIBC__) && !defined(__GNU__)) || defined(__BIONIC__)
     60  if (worker->thread_name != NULL) {
     61    // Linux and Android require names (with nul) fit in 16 chars, otherwise
     62    // pthread_setname_np() returns ERANGE (34).
     63    char thread_name[16];
     64    strncpy(thread_name, worker->thread_name, sizeof(thread_name) - 1);
     65    thread_name[sizeof(thread_name) - 1] = '\0';
     66    pthread_setname_np(pthread_self(), thread_name);
     67  }
     68 #endif
     69  pthread_mutex_lock(&worker->impl_->mutex_);
     70  for (;;) {
     71    while (worker->status_ == AVX_WORKER_STATUS_OK) {  // wait in idling mode
     72      pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_);
     73    }
     74    if (worker->status_ == AVX_WORKER_STATUS_WORKING) {
     75      // When worker->status_ is AVX_WORKER_STATUS_WORKING, the main thread
     76      // doesn't change worker->status_ and will wait until the worker changes
     77      // worker->status_ to AVX_WORKER_STATUS_OK. See change_state(). So the
     78      // worker can safely call execute() without holding worker->impl_->mutex_.
     79      // When the worker reacquires worker->impl_->mutex_, worker->status_ must
     80      // still be AVX_WORKER_STATUS_WORKING.
     81      pthread_mutex_unlock(&worker->impl_->mutex_);
     82      execute(worker);
     83      pthread_mutex_lock(&worker->impl_->mutex_);
     84      assert(worker->status_ == AVX_WORKER_STATUS_WORKING);
     85      worker->status_ = AVX_WORKER_STATUS_OK;
     86      // signal to the main thread that we're done (for sync())
     87      pthread_cond_signal(&worker->impl_->condition_);
     88    } else {
     89      assert(worker->status_ == AVX_WORKER_STATUS_NOT_OK);  // finish the worker
     90      break;
     91    }
     92  }
     93  pthread_mutex_unlock(&worker->impl_->mutex_);
     94  return THREAD_EXIT_SUCCESS;  // Thread is finished
     95 }
     96 
     97 // main thread state control
     98 static void change_state(AVxWorker *const worker, AVxWorkerStatus new_status) {
     99  // No-op when attempting to change state on a thread that didn't come up.
    100  // Checking status_ without acquiring the lock first would result in a data
    101  // race.
    102  if (worker->impl_ == NULL) return;
    103 
    104  pthread_mutex_lock(&worker->impl_->mutex_);
    105  if (worker->status_ >= AVX_WORKER_STATUS_OK) {
    106    // wait for the worker to finish
    107    while (worker->status_ != AVX_WORKER_STATUS_OK) {
    108      pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_);
    109    }
    110    // assign new status and release the working thread if needed
    111    if (new_status != AVX_WORKER_STATUS_OK) {
    112      worker->status_ = new_status;
    113      pthread_cond_signal(&worker->impl_->condition_);
    114    }
    115  }
    116  pthread_mutex_unlock(&worker->impl_->mutex_);
    117 }
    118 
    119 #endif  // CONFIG_MULTITHREAD
    120 
    121 //------------------------------------------------------------------------------
    122 
    123 static void init(AVxWorker *const worker) {
    124  memset(worker, 0, sizeof(*worker));
    125  worker->status_ = AVX_WORKER_STATUS_NOT_OK;
    126 }
    127 
    128 static int sync(AVxWorker *const worker) {
    129 #if CONFIG_MULTITHREAD
    130  change_state(worker, AVX_WORKER_STATUS_OK);
    131 #endif
    132  assert(worker->status_ <= AVX_WORKER_STATUS_OK);
    133  return !worker->had_error;
    134 }
    135 
    136 static int reset(AVxWorker *const worker) {
    137  int ok = 1;
    138  worker->had_error = 0;
    139  if (worker->status_ < AVX_WORKER_STATUS_OK) {
    140 #if CONFIG_MULTITHREAD
    141    worker->impl_ = (AVxWorkerImpl *)aom_calloc(1, sizeof(*worker->impl_));
    142    if (worker->impl_ == NULL) {
    143      return 0;
    144    }
    145    if (pthread_mutex_init(&worker->impl_->mutex_, NULL)) {
    146      goto Error;
    147    }
    148    if (pthread_cond_init(&worker->impl_->condition_, NULL)) {
    149      pthread_mutex_destroy(&worker->impl_->mutex_);
    150      goto Error;
    151    }
    152    pthread_attr_t attr;
    153    if (pthread_attr_init(&attr)) goto Error2;
    154    // Debug ASan builds require at least ~1MiB of stack; prevents
    155    // failures on macOS arm64 where the default is 512KiB.
    156    // See: https://crbug.com/aomedia/3379
    157 #if defined(AOM_ADDRESS_SANITIZER) && defined(__APPLE__) && AOM_ARCH_ARM && \
    158    !defined(NDEBUG)
    159    const size_t kMinStackSize = 1024 * 1024;
    160 #else
    161    const size_t kMinStackSize = 256 * 1024;
    162 #endif
    163    size_t stacksize;
    164    if (!pthread_attr_getstacksize(&attr, &stacksize)) {
    165      if (stacksize < kMinStackSize &&
    166          pthread_attr_setstacksize(&attr, kMinStackSize)) {
    167        pthread_attr_destroy(&attr);
    168        goto Error2;
    169      }
    170    }
    171    pthread_mutex_lock(&worker->impl_->mutex_);
    172    ok = !pthread_create(&worker->impl_->thread_, &attr, thread_loop, worker);
    173    if (ok) worker->status_ = AVX_WORKER_STATUS_OK;
    174    pthread_mutex_unlock(&worker->impl_->mutex_);
    175    pthread_attr_destroy(&attr);
    176    if (!ok) {
    177    Error2:
    178      pthread_mutex_destroy(&worker->impl_->mutex_);
    179      pthread_cond_destroy(&worker->impl_->condition_);
    180    Error:
    181      aom_free(worker->impl_);
    182      worker->impl_ = NULL;
    183      return 0;
    184    }
    185 #else
    186    worker->status_ = AVX_WORKER_STATUS_OK;
    187 #endif
    188  } else if (worker->status_ > AVX_WORKER_STATUS_OK) {
    189    ok = sync(worker);
    190  }
    191  assert(!ok || (worker->status_ == AVX_WORKER_STATUS_OK));
    192  return ok;
    193 }
    194 
    195 static void execute(AVxWorker *const worker) {
    196  if (worker->hook != NULL) {
    197    worker->had_error |= !worker->hook(worker->data1, worker->data2);
    198  }
    199 }
    200 
    201 static void launch(AVxWorker *const worker) {
    202 #if CONFIG_MULTITHREAD
    203  change_state(worker, AVX_WORKER_STATUS_WORKING);
    204 #else
    205  execute(worker);
    206 #endif
    207 }
    208 
    209 static void end(AVxWorker *const worker) {
    210 #if CONFIG_MULTITHREAD
    211  if (worker->impl_ != NULL) {
    212    change_state(worker, AVX_WORKER_STATUS_NOT_OK);
    213    pthread_join(worker->impl_->thread_, NULL);
    214    pthread_mutex_destroy(&worker->impl_->mutex_);
    215    pthread_cond_destroy(&worker->impl_->condition_);
    216    aom_free(worker->impl_);
    217    worker->impl_ = NULL;
    218  }
    219 #else
    220  worker->status_ = AVX_WORKER_STATUS_NOT_OK;
    221  assert(worker->impl_ == NULL);
    222 #endif
    223  assert(worker->status_ == AVX_WORKER_STATUS_NOT_OK);
    224 }
    225 
    226 //------------------------------------------------------------------------------
    227 
    228 static AVxWorkerInterface g_worker_interface = { init,   reset,   sync,
    229                                                 launch, execute, end };
    230 
    231 int aom_set_worker_interface(const AVxWorkerInterface *const winterface) {
    232  if (winterface == NULL || winterface->init == NULL ||
    233      winterface->reset == NULL || winterface->sync == NULL ||
    234      winterface->launch == NULL || winterface->execute == NULL ||
    235      winterface->end == NULL) {
    236    return 0;
    237  }
    238  g_worker_interface = *winterface;
    239  return 1;
    240 }
    241 
    242 const AVxWorkerInterface *aom_get_worker_interface(void) {
    243  return &g_worker_interface;
    244 }
    245 
    246 //------------------------------------------------------------------------------