xref: /aosp_15_r20/external/libvpx/vpx_util/vpx_thread.c (revision fb1b10ab9aebc7c7068eedab379b749d7e3900be)
1*fb1b10abSAndroid Build Coastguard Worker // Copyright 2013 Google Inc. All Rights Reserved.
2*fb1b10abSAndroid Build Coastguard Worker //
3*fb1b10abSAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license
4*fb1b10abSAndroid Build Coastguard Worker // that can be found in the COPYING file in the root of the source
5*fb1b10abSAndroid Build Coastguard Worker // tree. An additional intellectual property rights grant can be found
6*fb1b10abSAndroid Build Coastguard Worker // in the file PATENTS. All contributing project authors may
7*fb1b10abSAndroid Build Coastguard Worker // be found in the AUTHORS file in the root of the source tree.
8*fb1b10abSAndroid Build Coastguard Worker // -----------------------------------------------------------------------------
9*fb1b10abSAndroid Build Coastguard Worker //
10*fb1b10abSAndroid Build Coastguard Worker // Multi-threaded worker
11*fb1b10abSAndroid Build Coastguard Worker //
12*fb1b10abSAndroid Build Coastguard Worker // Original source:
13*fb1b10abSAndroid Build Coastguard Worker //  https://chromium.googlesource.com/webm/libwebp
14*fb1b10abSAndroid Build Coastguard Worker 
15*fb1b10abSAndroid Build Coastguard Worker // Enable GNU extensions in glibc so that we can call pthread_setname_np().
16*fb1b10abSAndroid Build Coastguard Worker // This must be before any #include statements.
17*fb1b10abSAndroid Build Coastguard Worker #ifndef _GNU_SOURCE
18*fb1b10abSAndroid Build Coastguard Worker #define _GNU_SOURCE
19*fb1b10abSAndroid Build Coastguard Worker #endif
20*fb1b10abSAndroid Build Coastguard Worker 
21*fb1b10abSAndroid Build Coastguard Worker #include <assert.h>
22*fb1b10abSAndroid Build Coastguard Worker #include <string.h>  // for memset()
23*fb1b10abSAndroid Build Coastguard Worker #include "./vpx_config.h"
24*fb1b10abSAndroid Build Coastguard Worker #include "./vpx_thread.h"
25*fb1b10abSAndroid Build Coastguard Worker #include "vpx_mem/vpx_mem.h"
26*fb1b10abSAndroid Build Coastguard Worker #include "vpx_util/vpx_pthread.h"
27*fb1b10abSAndroid Build Coastguard Worker 
28*fb1b10abSAndroid Build Coastguard Worker #if CONFIG_MULTITHREAD
29*fb1b10abSAndroid Build Coastguard Worker 
30*fb1b10abSAndroid Build Coastguard Worker struct VPxWorkerImpl {
31*fb1b10abSAndroid Build Coastguard Worker   pthread_mutex_t mutex_;
32*fb1b10abSAndroid Build Coastguard Worker   pthread_cond_t condition_;
33*fb1b10abSAndroid Build Coastguard Worker   pthread_t thread_;
34*fb1b10abSAndroid Build Coastguard Worker };
35*fb1b10abSAndroid Build Coastguard Worker 
36*fb1b10abSAndroid Build Coastguard Worker //------------------------------------------------------------------------------
37*fb1b10abSAndroid Build Coastguard Worker 
38*fb1b10abSAndroid Build Coastguard Worker static void execute(VPxWorker *const worker);  // Forward declaration.
39*fb1b10abSAndroid Build Coastguard Worker 
thread_loop(void * ptr)40*fb1b10abSAndroid Build Coastguard Worker static THREADFN thread_loop(void *ptr) {
41*fb1b10abSAndroid Build Coastguard Worker   VPxWorker *const worker = (VPxWorker *)ptr;
42*fb1b10abSAndroid Build Coastguard Worker #ifdef __APPLE__
43*fb1b10abSAndroid Build Coastguard Worker   if (worker->thread_name != NULL) {
44*fb1b10abSAndroid Build Coastguard Worker     // Apple's version of pthread_setname_np takes one argument and operates on
45*fb1b10abSAndroid Build Coastguard Worker     // the current thread only. The maximum size of the thread_name buffer was
46*fb1b10abSAndroid Build Coastguard Worker     // noted in the Chromium source code and was confirmed by experiments. If
47*fb1b10abSAndroid Build Coastguard Worker     // thread_name is too long, pthread_setname_np returns -1 with errno
48*fb1b10abSAndroid Build Coastguard Worker     // ENAMETOOLONG (63).
49*fb1b10abSAndroid Build Coastguard Worker     char thread_name[64];
50*fb1b10abSAndroid Build Coastguard Worker     strncpy(thread_name, worker->thread_name, sizeof(thread_name) - 1);
51*fb1b10abSAndroid Build Coastguard Worker     thread_name[sizeof(thread_name) - 1] = '\0';
52*fb1b10abSAndroid Build Coastguard Worker     pthread_setname_np(thread_name);
53*fb1b10abSAndroid Build Coastguard Worker   }
54*fb1b10abSAndroid Build Coastguard Worker #elif (defined(__GLIBC__) && !defined(__GNU__)) || defined(__BIONIC__)
55*fb1b10abSAndroid Build Coastguard Worker   if (worker->thread_name != NULL) {
56*fb1b10abSAndroid Build Coastguard Worker     // Linux and Android require names (with nul) fit in 16 chars, otherwise
57*fb1b10abSAndroid Build Coastguard Worker     // pthread_setname_np() returns ERANGE (34).
58*fb1b10abSAndroid Build Coastguard Worker     char thread_name[16];
59*fb1b10abSAndroid Build Coastguard Worker     strncpy(thread_name, worker->thread_name, sizeof(thread_name) - 1);
60*fb1b10abSAndroid Build Coastguard Worker     thread_name[sizeof(thread_name) - 1] = '\0';
61*fb1b10abSAndroid Build Coastguard Worker     pthread_setname_np(pthread_self(), thread_name);
62*fb1b10abSAndroid Build Coastguard Worker   }
63*fb1b10abSAndroid Build Coastguard Worker #endif
64*fb1b10abSAndroid Build Coastguard Worker   pthread_mutex_lock(&worker->impl_->mutex_);
65*fb1b10abSAndroid Build Coastguard Worker   for (;;) {
66*fb1b10abSAndroid Build Coastguard Worker     while (worker->status_ == VPX_WORKER_STATUS_OK) {  // wait in idling mode
67*fb1b10abSAndroid Build Coastguard Worker       pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_);
68*fb1b10abSAndroid Build Coastguard Worker     }
69*fb1b10abSAndroid Build Coastguard Worker     if (worker->status_ == VPX_WORKER_STATUS_WORKING) {
70*fb1b10abSAndroid Build Coastguard Worker       // When worker->status_ is VPX_WORKER_STATUS_WORKING, the main thread
71*fb1b10abSAndroid Build Coastguard Worker       // doesn't change worker->status_ and will wait until the worker changes
72*fb1b10abSAndroid Build Coastguard Worker       // worker->status_ to VPX_WORKER_STATUS_OK. See change_state(). So the
73*fb1b10abSAndroid Build Coastguard Worker       // worker can safely call execute() without holding worker->impl_->mutex_.
74*fb1b10abSAndroid Build Coastguard Worker       // When the worker reacquires worker->impl_->mutex_, worker->status_ must
75*fb1b10abSAndroid Build Coastguard Worker       // still be VPX_WORKER_STATUS_WORKING.
76*fb1b10abSAndroid Build Coastguard Worker       pthread_mutex_unlock(&worker->impl_->mutex_);
77*fb1b10abSAndroid Build Coastguard Worker       execute(worker);
78*fb1b10abSAndroid Build Coastguard Worker       pthread_mutex_lock(&worker->impl_->mutex_);
79*fb1b10abSAndroid Build Coastguard Worker       assert(worker->status_ == VPX_WORKER_STATUS_WORKING);
80*fb1b10abSAndroid Build Coastguard Worker       worker->status_ = VPX_WORKER_STATUS_OK;
81*fb1b10abSAndroid Build Coastguard Worker       // signal to the main thread that we're done (for sync())
82*fb1b10abSAndroid Build Coastguard Worker       pthread_cond_signal(&worker->impl_->condition_);
83*fb1b10abSAndroid Build Coastguard Worker     } else {
84*fb1b10abSAndroid Build Coastguard Worker       assert(worker->status_ == VPX_WORKER_STATUS_NOT_OK);  // finish the worker
85*fb1b10abSAndroid Build Coastguard Worker       break;
86*fb1b10abSAndroid Build Coastguard Worker     }
87*fb1b10abSAndroid Build Coastguard Worker   }
88*fb1b10abSAndroid Build Coastguard Worker   pthread_mutex_unlock(&worker->impl_->mutex_);
89*fb1b10abSAndroid Build Coastguard Worker   return THREAD_EXIT_SUCCESS;  // Thread is finished
90*fb1b10abSAndroid Build Coastguard Worker }
91*fb1b10abSAndroid Build Coastguard Worker 
92*fb1b10abSAndroid Build Coastguard Worker // main thread state control
change_state(VPxWorker * const worker,VPxWorkerStatus new_status)93*fb1b10abSAndroid Build Coastguard Worker static void change_state(VPxWorker *const worker, VPxWorkerStatus new_status) {
94*fb1b10abSAndroid Build Coastguard Worker   // No-op when attempting to change state on a thread that didn't come up.
95*fb1b10abSAndroid Build Coastguard Worker   // Checking status_ without acquiring the lock first would result in a data
96*fb1b10abSAndroid Build Coastguard Worker   // race.
97*fb1b10abSAndroid Build Coastguard Worker   if (worker->impl_ == NULL) return;
98*fb1b10abSAndroid Build Coastguard Worker 
99*fb1b10abSAndroid Build Coastguard Worker   pthread_mutex_lock(&worker->impl_->mutex_);
100*fb1b10abSAndroid Build Coastguard Worker   if (worker->status_ >= VPX_WORKER_STATUS_OK) {
101*fb1b10abSAndroid Build Coastguard Worker     // wait for the worker to finish
102*fb1b10abSAndroid Build Coastguard Worker     while (worker->status_ != VPX_WORKER_STATUS_OK) {
103*fb1b10abSAndroid Build Coastguard Worker       pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_);
104*fb1b10abSAndroid Build Coastguard Worker     }
105*fb1b10abSAndroid Build Coastguard Worker     // assign new status and release the working thread if needed
106*fb1b10abSAndroid Build Coastguard Worker     if (new_status != VPX_WORKER_STATUS_OK) {
107*fb1b10abSAndroid Build Coastguard Worker       worker->status_ = new_status;
108*fb1b10abSAndroid Build Coastguard Worker       pthread_cond_signal(&worker->impl_->condition_);
109*fb1b10abSAndroid Build Coastguard Worker     }
110*fb1b10abSAndroid Build Coastguard Worker   }
111*fb1b10abSAndroid Build Coastguard Worker   pthread_mutex_unlock(&worker->impl_->mutex_);
112*fb1b10abSAndroid Build Coastguard Worker }
113*fb1b10abSAndroid Build Coastguard Worker 
114*fb1b10abSAndroid Build Coastguard Worker #endif  // CONFIG_MULTITHREAD
115*fb1b10abSAndroid Build Coastguard Worker 
116*fb1b10abSAndroid Build Coastguard Worker //------------------------------------------------------------------------------
117*fb1b10abSAndroid Build Coastguard Worker 
init(VPxWorker * const worker)118*fb1b10abSAndroid Build Coastguard Worker static void init(VPxWorker *const worker) {
119*fb1b10abSAndroid Build Coastguard Worker   memset(worker, 0, sizeof(*worker));
120*fb1b10abSAndroid Build Coastguard Worker   worker->status_ = VPX_WORKER_STATUS_NOT_OK;
121*fb1b10abSAndroid Build Coastguard Worker }
122*fb1b10abSAndroid Build Coastguard Worker 
sync(VPxWorker * const worker)123*fb1b10abSAndroid Build Coastguard Worker static int sync(VPxWorker *const worker) {
124*fb1b10abSAndroid Build Coastguard Worker #if CONFIG_MULTITHREAD
125*fb1b10abSAndroid Build Coastguard Worker   change_state(worker, VPX_WORKER_STATUS_OK);
126*fb1b10abSAndroid Build Coastguard Worker #endif
127*fb1b10abSAndroid Build Coastguard Worker   assert(worker->status_ <= VPX_WORKER_STATUS_OK);
128*fb1b10abSAndroid Build Coastguard Worker   return !worker->had_error;
129*fb1b10abSAndroid Build Coastguard Worker }
130*fb1b10abSAndroid Build Coastguard Worker 
reset(VPxWorker * const worker)131*fb1b10abSAndroid Build Coastguard Worker static int reset(VPxWorker *const worker) {
132*fb1b10abSAndroid Build Coastguard Worker   int ok = 1;
133*fb1b10abSAndroid Build Coastguard Worker   worker->had_error = 0;
134*fb1b10abSAndroid Build Coastguard Worker   if (worker->status_ < VPX_WORKER_STATUS_OK) {
135*fb1b10abSAndroid Build Coastguard Worker #if CONFIG_MULTITHREAD
136*fb1b10abSAndroid Build Coastguard Worker     worker->impl_ = (VPxWorkerImpl *)vpx_calloc(1, sizeof(*worker->impl_));
137*fb1b10abSAndroid Build Coastguard Worker     if (worker->impl_ == NULL) {
138*fb1b10abSAndroid Build Coastguard Worker       return 0;
139*fb1b10abSAndroid Build Coastguard Worker     }
140*fb1b10abSAndroid Build Coastguard Worker     if (pthread_mutex_init(&worker->impl_->mutex_, NULL)) {
141*fb1b10abSAndroid Build Coastguard Worker       goto Error;
142*fb1b10abSAndroid Build Coastguard Worker     }
143*fb1b10abSAndroid Build Coastguard Worker     if (pthread_cond_init(&worker->impl_->condition_, NULL)) {
144*fb1b10abSAndroid Build Coastguard Worker       pthread_mutex_destroy(&worker->impl_->mutex_);
145*fb1b10abSAndroid Build Coastguard Worker       goto Error;
146*fb1b10abSAndroid Build Coastguard Worker     }
147*fb1b10abSAndroid Build Coastguard Worker     pthread_mutex_lock(&worker->impl_->mutex_);
148*fb1b10abSAndroid Build Coastguard Worker     ok = !pthread_create(&worker->impl_->thread_, NULL, thread_loop, worker);
149*fb1b10abSAndroid Build Coastguard Worker     if (ok) worker->status_ = VPX_WORKER_STATUS_OK;
150*fb1b10abSAndroid Build Coastguard Worker     pthread_mutex_unlock(&worker->impl_->mutex_);
151*fb1b10abSAndroid Build Coastguard Worker     if (!ok) {
152*fb1b10abSAndroid Build Coastguard Worker       pthread_mutex_destroy(&worker->impl_->mutex_);
153*fb1b10abSAndroid Build Coastguard Worker       pthread_cond_destroy(&worker->impl_->condition_);
154*fb1b10abSAndroid Build Coastguard Worker     Error:
155*fb1b10abSAndroid Build Coastguard Worker       vpx_free(worker->impl_);
156*fb1b10abSAndroid Build Coastguard Worker       worker->impl_ = NULL;
157*fb1b10abSAndroid Build Coastguard Worker       return 0;
158*fb1b10abSAndroid Build Coastguard Worker     }
159*fb1b10abSAndroid Build Coastguard Worker #else
160*fb1b10abSAndroid Build Coastguard Worker     worker->status_ = VPX_WORKER_STATUS_OK;
161*fb1b10abSAndroid Build Coastguard Worker #endif
162*fb1b10abSAndroid Build Coastguard Worker   } else if (worker->status_ > VPX_WORKER_STATUS_OK) {
163*fb1b10abSAndroid Build Coastguard Worker     ok = sync(worker);
164*fb1b10abSAndroid Build Coastguard Worker   }
165*fb1b10abSAndroid Build Coastguard Worker   assert(!ok || (worker->status_ == VPX_WORKER_STATUS_OK));
166*fb1b10abSAndroid Build Coastguard Worker   return ok;
167*fb1b10abSAndroid Build Coastguard Worker }
168*fb1b10abSAndroid Build Coastguard Worker 
execute(VPxWorker * const worker)169*fb1b10abSAndroid Build Coastguard Worker static void execute(VPxWorker *const worker) {
170*fb1b10abSAndroid Build Coastguard Worker   if (worker->hook != NULL) {
171*fb1b10abSAndroid Build Coastguard Worker     worker->had_error |= !worker->hook(worker->data1, worker->data2);
172*fb1b10abSAndroid Build Coastguard Worker   }
173*fb1b10abSAndroid Build Coastguard Worker }
174*fb1b10abSAndroid Build Coastguard Worker 
launch(VPxWorker * const worker)175*fb1b10abSAndroid Build Coastguard Worker static void launch(VPxWorker *const worker) {
176*fb1b10abSAndroid Build Coastguard Worker #if CONFIG_MULTITHREAD
177*fb1b10abSAndroid Build Coastguard Worker   change_state(worker, VPX_WORKER_STATUS_WORKING);
178*fb1b10abSAndroid Build Coastguard Worker #else
179*fb1b10abSAndroid Build Coastguard Worker   execute(worker);
180*fb1b10abSAndroid Build Coastguard Worker #endif
181*fb1b10abSAndroid Build Coastguard Worker }
182*fb1b10abSAndroid Build Coastguard Worker 
end(VPxWorker * const worker)183*fb1b10abSAndroid Build Coastguard Worker static void end(VPxWorker *const worker) {
184*fb1b10abSAndroid Build Coastguard Worker #if CONFIG_MULTITHREAD
185*fb1b10abSAndroid Build Coastguard Worker   if (worker->impl_ != NULL) {
186*fb1b10abSAndroid Build Coastguard Worker     change_state(worker, VPX_WORKER_STATUS_NOT_OK);
187*fb1b10abSAndroid Build Coastguard Worker     pthread_join(worker->impl_->thread_, NULL);
188*fb1b10abSAndroid Build Coastguard Worker     pthread_mutex_destroy(&worker->impl_->mutex_);
189*fb1b10abSAndroid Build Coastguard Worker     pthread_cond_destroy(&worker->impl_->condition_);
190*fb1b10abSAndroid Build Coastguard Worker     vpx_free(worker->impl_);
191*fb1b10abSAndroid Build Coastguard Worker     worker->impl_ = NULL;
192*fb1b10abSAndroid Build Coastguard Worker   }
193*fb1b10abSAndroid Build Coastguard Worker #else
194*fb1b10abSAndroid Build Coastguard Worker   worker->status_ = VPX_WORKER_STATUS_NOT_OK;
195*fb1b10abSAndroid Build Coastguard Worker   assert(worker->impl_ == NULL);
196*fb1b10abSAndroid Build Coastguard Worker #endif
197*fb1b10abSAndroid Build Coastguard Worker   assert(worker->status_ == VPX_WORKER_STATUS_NOT_OK);
198*fb1b10abSAndroid Build Coastguard Worker }
199*fb1b10abSAndroid Build Coastguard Worker 
200*fb1b10abSAndroid Build Coastguard Worker //------------------------------------------------------------------------------
201*fb1b10abSAndroid Build Coastguard Worker 
202*fb1b10abSAndroid Build Coastguard Worker static VPxWorkerInterface g_worker_interface = { init,   reset,   sync,
203*fb1b10abSAndroid Build Coastguard Worker                                                  launch, execute, end };
204*fb1b10abSAndroid Build Coastguard Worker 
vpx_set_worker_interface(const VPxWorkerInterface * const winterface)205*fb1b10abSAndroid Build Coastguard Worker int vpx_set_worker_interface(const VPxWorkerInterface *const winterface) {
206*fb1b10abSAndroid Build Coastguard Worker   if (winterface == NULL || winterface->init == NULL ||
207*fb1b10abSAndroid Build Coastguard Worker       winterface->reset == NULL || winterface->sync == NULL ||
208*fb1b10abSAndroid Build Coastguard Worker       winterface->launch == NULL || winterface->execute == NULL ||
209*fb1b10abSAndroid Build Coastguard Worker       winterface->end == NULL) {
210*fb1b10abSAndroid Build Coastguard Worker     return 0;
211*fb1b10abSAndroid Build Coastguard Worker   }
212*fb1b10abSAndroid Build Coastguard Worker   g_worker_interface = *winterface;
213*fb1b10abSAndroid Build Coastguard Worker   return 1;
214*fb1b10abSAndroid Build Coastguard Worker }
215*fb1b10abSAndroid Build Coastguard Worker 
vpx_get_worker_interface(void)216*fb1b10abSAndroid Build Coastguard Worker const VPxWorkerInterface *vpx_get_worker_interface(void) {
217*fb1b10abSAndroid Build Coastguard Worker   return &g_worker_interface;
218*fb1b10abSAndroid Build Coastguard Worker }
219*fb1b10abSAndroid Build Coastguard Worker 
220*fb1b10abSAndroid Build Coastguard Worker //------------------------------------------------------------------------------
221