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