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 #include <assert.h>
16 #include <string.h> // for memset()
17 #include "./vpx_thread.h"
18 #include "vpx_mem/vpx_mem.h"
19
20 #if CONFIG_MULTITHREAD
21
22 struct VPxWorkerImpl {
23 pthread_mutex_t mutex_;
24 pthread_cond_t condition_;
25 pthread_t thread_;
26 };
27
28 //------------------------------------------------------------------------------
29
30 static void execute(VPxWorker *const worker); // Forward declaration.
31
thread_loop(void * ptr)32 static THREADFN thread_loop(void *ptr) {
33 VPxWorker *const worker = (VPxWorker *)ptr;
34 int done = 0;
35 while (!done) {
36 pthread_mutex_lock(&worker->impl_->mutex_);
37 while (worker->status_ == OK) { // wait in idling mode
38 pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_);
39 }
40 if (worker->status_ == WORK) {
41 execute(worker);
42 worker->status_ = OK;
43 } else if (worker->status_ == NOT_OK) { // finish the worker
44 done = 1;
45 }
46 // signal to the main thread that we're done (for sync())
47 pthread_cond_signal(&worker->impl_->condition_);
48 pthread_mutex_unlock(&worker->impl_->mutex_);
49 }
50 return THREAD_RETURN(NULL); // Thread is finished
51 }
52
53 // main thread state control
change_state(VPxWorker * const worker,VPxWorkerStatus new_status)54 static void change_state(VPxWorker *const worker, VPxWorkerStatus new_status) {
55 // No-op when attempting to change state on a thread that didn't come up.
56 // Checking status_ without acquiring the lock first would result in a data
57 // race.
58 if (worker->impl_ == NULL) return;
59
60 pthread_mutex_lock(&worker->impl_->mutex_);
61 if (worker->status_ >= OK) {
62 // wait for the worker to finish
63 while (worker->status_ != OK) {
64 pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_);
65 }
66 // assign new status and release the working thread if needed
67 if (new_status != OK) {
68 worker->status_ = new_status;
69 pthread_cond_signal(&worker->impl_->condition_);
70 }
71 }
72 pthread_mutex_unlock(&worker->impl_->mutex_);
73 }
74
75 #endif // CONFIG_MULTITHREAD
76
77 //------------------------------------------------------------------------------
78
init(VPxWorker * const worker)79 static void init(VPxWorker *const worker) {
80 memset(worker, 0, sizeof(*worker));
81 worker->status_ = NOT_OK;
82 }
83
sync(VPxWorker * const worker)84 static int sync(VPxWorker *const worker) {
85 #if CONFIG_MULTITHREAD
86 change_state(worker, OK);
87 #endif
88 assert(worker->status_ <= OK);
89 return !worker->had_error;
90 }
91
reset(VPxWorker * const worker)92 static int reset(VPxWorker *const worker) {
93 int ok = 1;
94 worker->had_error = 0;
95 if (worker->status_ < OK) {
96 #if CONFIG_MULTITHREAD
97 worker->impl_ = (VPxWorkerImpl *)vpx_calloc(1, sizeof(*worker->impl_));
98 if (worker->impl_ == NULL) {
99 return 0;
100 }
101 if (pthread_mutex_init(&worker->impl_->mutex_, NULL)) {
102 goto Error;
103 }
104 if (pthread_cond_init(&worker->impl_->condition_, NULL)) {
105 pthread_mutex_destroy(&worker->impl_->mutex_);
106 goto Error;
107 }
108 pthread_mutex_lock(&worker->impl_->mutex_);
109 ok = !pthread_create(&worker->impl_->thread_, NULL, thread_loop, worker);
110 if (ok) worker->status_ = OK;
111 pthread_mutex_unlock(&worker->impl_->mutex_);
112 if (!ok) {
113 pthread_mutex_destroy(&worker->impl_->mutex_);
114 pthread_cond_destroy(&worker->impl_->condition_);
115 Error:
116 vpx_free(worker->impl_);
117 worker->impl_ = NULL;
118 return 0;
119 }
120 #else
121 worker->status_ = OK;
122 #endif
123 } else if (worker->status_ > OK) {
124 ok = sync(worker);
125 }
126 assert(!ok || (worker->status_ == OK));
127 return ok;
128 }
129
execute(VPxWorker * const worker)130 static void execute(VPxWorker *const worker) {
131 if (worker->hook != NULL) {
132 worker->had_error |= !worker->hook(worker->data1, worker->data2);
133 }
134 }
135
launch(VPxWorker * const worker)136 static void launch(VPxWorker *const worker) {
137 #if CONFIG_MULTITHREAD
138 change_state(worker, WORK);
139 #else
140 execute(worker);
141 #endif
142 }
143
end(VPxWorker * const worker)144 static void end(VPxWorker *const worker) {
145 #if CONFIG_MULTITHREAD
146 if (worker->impl_ != NULL) {
147 change_state(worker, NOT_OK);
148 pthread_join(worker->impl_->thread_, NULL);
149 pthread_mutex_destroy(&worker->impl_->mutex_);
150 pthread_cond_destroy(&worker->impl_->condition_);
151 vpx_free(worker->impl_);
152 worker->impl_ = NULL;
153 }
154 #else
155 worker->status_ = NOT_OK;
156 assert(worker->impl_ == NULL);
157 #endif
158 assert(worker->status_ == NOT_OK);
159 }
160
161 //------------------------------------------------------------------------------
162
163 static VPxWorkerInterface g_worker_interface = { init, reset, sync,
164 launch, execute, end };
165
vpx_set_worker_interface(const VPxWorkerInterface * const winterface)166 int vpx_set_worker_interface(const VPxWorkerInterface *const winterface) {
167 if (winterface == NULL || winterface->init == NULL ||
168 winterface->reset == NULL || winterface->sync == NULL ||
169 winterface->launch == NULL || winterface->execute == NULL ||
170 winterface->end == NULL) {
171 return 0;
172 }
173 g_worker_interface = *winterface;
174 return 1;
175 }
176
vpx_get_worker_interface(void)177 const VPxWorkerInterface *vpx_get_worker_interface(void) {
178 return &g_worker_interface;
179 }
180
181 //------------------------------------------------------------------------------
182