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