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