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