Bug 1874684 - Part 33: Defer allocation of options object for CalendarDateFromFields...
[gecko.git] / third_party / aom / aom_util / aom_thread.c
blobbdf2b7dfa6c5ace19bb878a8aa82ce459769cb7a
1 /*
2 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
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.
12 // Multi-threaded worker
14 // Original source:
15 // https://chromium.googlesource.com/webm/libwebp
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
23 #include <assert.h>
24 #include <string.h> // for memset()
26 #include "config/aom_config.h"
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"
33 #if CONFIG_MULTITHREAD
35 struct AVxWorkerImpl {
36 pthread_mutex_t mutex_;
37 pthread_cond_t condition_;
38 pthread_t thread_;
41 //------------------------------------------------------------------------------
43 static void execute(AVxWorker *const worker); // Forward declaration.
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);
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);
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_);
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;
93 pthread_mutex_unlock(&worker->impl_->mutex_);
94 return THREAD_EXIT_SUCCESS; // Thread is finished
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;
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_);
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_);
116 pthread_mutex_unlock(&worker->impl_->mutex_);
119 #endif // CONFIG_MULTITHREAD
121 //------------------------------------------------------------------------------
123 static void init(AVxWorker *const worker) {
124 memset(worker, 0, sizeof(*worker));
125 worker->status_ = AVX_WORKER_STATUS_NOT_OK;
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;
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;
145 if (pthread_mutex_init(&worker->impl_->mutex_, NULL)) {
146 goto Error;
148 if (pthread_cond_init(&worker->impl_->condition_, NULL)) {
149 pthread_mutex_destroy(&worker->impl_->mutex_);
150 goto Error;
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 size_t stacksize;
160 if (!pthread_attr_getstacksize(&attr, &stacksize)) {
161 const size_t kMinStackSize = 1 << 20; // 1 MiB
162 if (stacksize < kMinStackSize &&
163 pthread_attr_setstacksize(&attr, kMinStackSize)) {
164 pthread_attr_destroy(&attr);
165 goto Error2;
168 #endif
169 pthread_mutex_lock(&worker->impl_->mutex_);
170 ok = !pthread_create(&worker->impl_->thread_, &attr, thread_loop, worker);
171 if (ok) worker->status_ = AVX_WORKER_STATUS_OK;
172 pthread_mutex_unlock(&worker->impl_->mutex_);
173 pthread_attr_destroy(&attr);
174 if (!ok) {
175 Error2:
176 pthread_mutex_destroy(&worker->impl_->mutex_);
177 pthread_cond_destroy(&worker->impl_->condition_);
178 Error:
179 aom_free(worker->impl_);
180 worker->impl_ = NULL;
181 return 0;
183 #else
184 worker->status_ = AVX_WORKER_STATUS_OK;
185 #endif
186 } else if (worker->status_ > AVX_WORKER_STATUS_OK) {
187 ok = sync(worker);
189 assert(!ok || (worker->status_ == AVX_WORKER_STATUS_OK));
190 return ok;
193 static void execute(AVxWorker *const worker) {
194 if (worker->hook != NULL) {
195 worker->had_error |= !worker->hook(worker->data1, worker->data2);
199 static void launch(AVxWorker *const worker) {
200 #if CONFIG_MULTITHREAD
201 change_state(worker, AVX_WORKER_STATUS_WORKING);
202 #else
203 execute(worker);
204 #endif
207 static void end(AVxWorker *const worker) {
208 #if CONFIG_MULTITHREAD
209 if (worker->impl_ != NULL) {
210 change_state(worker, AVX_WORKER_STATUS_NOT_OK);
211 pthread_join(worker->impl_->thread_, NULL);
212 pthread_mutex_destroy(&worker->impl_->mutex_);
213 pthread_cond_destroy(&worker->impl_->condition_);
214 aom_free(worker->impl_);
215 worker->impl_ = NULL;
217 #else
218 worker->status_ = AVX_WORKER_STATUS_NOT_OK;
219 assert(worker->impl_ == NULL);
220 #endif
221 assert(worker->status_ == AVX_WORKER_STATUS_NOT_OK);
224 //------------------------------------------------------------------------------
226 static AVxWorkerInterface g_worker_interface = { init, reset, sync,
227 launch, execute, end };
229 int aom_set_worker_interface(const AVxWorkerInterface *const winterface) {
230 if (winterface == NULL || winterface->init == NULL ||
231 winterface->reset == NULL || winterface->sync == NULL ||
232 winterface->launch == NULL || winterface->execute == NULL ||
233 winterface->end == NULL) {
234 return 0;
236 g_worker_interface = *winterface;
237 return 1;
240 const AVxWorkerInterface *aom_get_worker_interface(void) {
241 return &g_worker_interface;
244 //------------------------------------------------------------------------------