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