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 //------------------------------------------------------------------------------