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