Home | History | Annotate | Download | only in vpx_util
      1 // Copyright 2013 Google Inc. All Rights Reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style license
      4 // that can be found in the COPYING file in the root of the source
      5 // tree. An additional intellectual property rights grant can be found
      6 // in the file PATENTS. All contributing project authors may
      7 // be found in the AUTHORS file in the root of the source tree.
      8 // -----------------------------------------------------------------------------
      9 //
     10 // Multi-threaded worker
     11 //
     12 // Original source:
     13 //  https://chromium.googlesource.com/webm/libwebp
     14 
     15 #include <assert.h>
     16 #include <string.h>  // for memset()
     17 #include "./vpx_thread.h"
     18 #include "vpx_mem/vpx_mem.h"
     19 
     20 #if CONFIG_MULTITHREAD
     21 
     22 struct VPxWorkerImpl {
     23   pthread_mutex_t mutex_;
     24   pthread_cond_t condition_;
     25   pthread_t thread_;
     26 };
     27 
     28 //------------------------------------------------------------------------------
     29 
     30 static void execute(VPxWorker *const worker);  // Forward declaration.
     31 
     32 static THREADFN thread_loop(void *ptr) {
     33   VPxWorker *const worker = (VPxWorker *)ptr;
     34   int done = 0;
     35   while (!done) {
     36     pthread_mutex_lock(&worker->impl_->mutex_);
     37     while (worker->status_ == OK) {  // wait in idling mode
     38       pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_);
     39     }
     40     if (worker->status_ == WORK) {
     41       execute(worker);
     42       worker->status_ = OK;
     43     } else if (worker->status_ == NOT_OK) {  // finish the worker
     44       done = 1;
     45     }
     46     // signal to the main thread that we're done (for sync())
     47     pthread_cond_signal(&worker->impl_->condition_);
     48     pthread_mutex_unlock(&worker->impl_->mutex_);
     49   }
     50   return THREAD_RETURN(NULL);  // Thread is finished
     51 }
     52 
     53 // main thread state control
     54 static void change_state(VPxWorker *const worker, VPxWorkerStatus new_status) {
     55   // No-op when attempting to change state on a thread that didn't come up.
     56   // Checking status_ without acquiring the lock first would result in a data
     57   // race.
     58   if (worker->impl_ == NULL) return;
     59 
     60   pthread_mutex_lock(&worker->impl_->mutex_);
     61   if (worker->status_ >= OK) {
     62     // wait for the worker to finish
     63     while (worker->status_ != OK) {
     64       pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_);
     65     }
     66     // assign new status and release the working thread if needed
     67     if (new_status != OK) {
     68       worker->status_ = new_status;
     69       pthread_cond_signal(&worker->impl_->condition_);
     70     }
     71   }
     72   pthread_mutex_unlock(&worker->impl_->mutex_);
     73 }
     74 
     75 #endif  // CONFIG_MULTITHREAD
     76 
     77 //------------------------------------------------------------------------------
     78 
     79 static void init(VPxWorker *const worker) {
     80   memset(worker, 0, sizeof(*worker));
     81   worker->status_ = NOT_OK;
     82 }
     83 
     84 static int sync(VPxWorker *const worker) {
     85 #if CONFIG_MULTITHREAD
     86   change_state(worker, OK);
     87 #endif
     88   assert(worker->status_ <= OK);
     89   return !worker->had_error;
     90 }
     91 
     92 static int reset(VPxWorker *const worker) {
     93   int ok = 1;
     94   worker->had_error = 0;
     95   if (worker->status_ < OK) {
     96 #if CONFIG_MULTITHREAD
     97     worker->impl_ = (VPxWorkerImpl *)vpx_calloc(1, sizeof(*worker->impl_));
     98     if (worker->impl_ == NULL) {
     99       return 0;
    100     }
    101     if (pthread_mutex_init(&worker->impl_->mutex_, NULL)) {
    102       goto Error;
    103     }
    104     if (pthread_cond_init(&worker->impl_->condition_, NULL)) {
    105       pthread_mutex_destroy(&worker->impl_->mutex_);
    106       goto Error;
    107     }
    108     pthread_mutex_lock(&worker->impl_->mutex_);
    109     ok = !pthread_create(&worker->impl_->thread_, NULL, thread_loop, worker);
    110     if (ok) worker->status_ = OK;
    111     pthread_mutex_unlock(&worker->impl_->mutex_);
    112     if (!ok) {
    113       pthread_mutex_destroy(&worker->impl_->mutex_);
    114       pthread_cond_destroy(&worker->impl_->condition_);
    115     Error:
    116       vpx_free(worker->impl_);
    117       worker->impl_ = NULL;
    118       return 0;
    119     }
    120 #else
    121     worker->status_ = OK;
    122 #endif
    123   } else if (worker->status_ > OK) {
    124     ok = sync(worker);
    125   }
    126   assert(!ok || (worker->status_ == OK));
    127   return ok;
    128 }
    129 
    130 static void execute(VPxWorker *const worker) {
    131   if (worker->hook != NULL) {
    132     worker->had_error |= !worker->hook(worker->data1, worker->data2);
    133   }
    134 }
    135 
    136 static void launch(VPxWorker *const worker) {
    137 #if CONFIG_MULTITHREAD
    138   change_state(worker, WORK);
    139 #else
    140   execute(worker);
    141 #endif
    142 }
    143 
    144 static void end(VPxWorker *const worker) {
    145 #if CONFIG_MULTITHREAD
    146   if (worker->impl_ != NULL) {
    147     change_state(worker, NOT_OK);
    148     pthread_join(worker->impl_->thread_, NULL);
    149     pthread_mutex_destroy(&worker->impl_->mutex_);
    150     pthread_cond_destroy(&worker->impl_->condition_);
    151     vpx_free(worker->impl_);
    152     worker->impl_ = NULL;
    153   }
    154 #else
    155   worker->status_ = NOT_OK;
    156   assert(worker->impl_ == NULL);
    157 #endif
    158   assert(worker->status_ == NOT_OK);
    159 }
    160 
    161 //------------------------------------------------------------------------------
    162 
    163 static VPxWorkerInterface g_worker_interface = { init,   reset,   sync,
    164                                                  launch, execute, end };
    165 
    166 int vpx_set_worker_interface(const VPxWorkerInterface *const winterface) {
    167   if (winterface == NULL || winterface->init == NULL ||
    168       winterface->reset == NULL || winterface->sync == NULL ||
    169       winterface->launch == NULL || winterface->execute == NULL ||
    170       winterface->end == NULL) {
    171     return 0;
    172   }
    173   g_worker_interface = *winterface;
    174   return 1;
    175 }
    176 
    177 const VPxWorkerInterface *vpx_get_worker_interface(void) {
    178   return &g_worker_interface;
    179 }
    180 
    181 //------------------------------------------------------------------------------
    182