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