Coverage Report

Created: 2022-08-24 06:11

/src/aom/aom_util/aom_thread.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
3
 *
4
 * This source code is subject to the terms of the BSD 2 Clause License and
5
 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6
 * was not distributed with this source code in the LICENSE file, you can
7
 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8
 * Media Patent License 1.0 was not distributed with this source code in the
9
 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10
 */
11
//
12
// Multi-threaded worker
13
//
14
// Original source:
15
//  https://chromium.googlesource.com/webm/libwebp
16
17
// Enable GNU extensions in glibc so that we can call pthread_setname_np().
18
// This must be before any #include statements.
19
#ifndef _GNU_SOURCE
20
#define _GNU_SOURCE
21
#endif
22
23
#include <assert.h>
24
#include <string.h>  // for memset()
25
26
#include "aom_mem/aom_mem.h"
27
#include "aom_util/aom_thread.h"
28
29
#if CONFIG_MULTITHREAD
30
31
struct AVxWorkerImpl {
32
  pthread_mutex_t mutex_;
33
  pthread_cond_t condition_;
34
  pthread_t thread_;
35
};
36
37
//------------------------------------------------------------------------------
38
39
static void execute(AVxWorker *const worker);  // Forward declaration.
40
41
0
static THREADFN thread_loop(void *ptr) {
42
0
  AVxWorker *const worker = (AVxWorker *)ptr;
43
#ifdef __APPLE__
44
  if (worker->thread_name != NULL) {
45
    // Apple's version of pthread_setname_np takes one argument and operates on
46
    // the current thread only. The maximum size of the thread_name buffer was
47
    // noted in the Chromium source code and was confirmed by experiments. If
48
    // thread_name is too long, pthread_setname_np returns -1 with errno
49
    // ENAMETOOLONG (63).
50
    char thread_name[64];
51
    strncpy(thread_name, worker->thread_name, sizeof(thread_name) - 1);
52
    thread_name[sizeof(thread_name) - 1] = '\0';
53
    pthread_setname_np(thread_name);
54
  }
55
#elif defined(__GLIBC__) || defined(__BIONIC__)
56
0
  if (worker->thread_name != NULL) {
57
    // Linux and Android require names (with nul) fit in 16 chars, otherwise
58
    // pthread_setname_np() returns ERANGE (34).
59
0
    char thread_name[16];
60
0
    strncpy(thread_name, worker->thread_name, sizeof(thread_name) - 1);
61
0
    thread_name[sizeof(thread_name) - 1] = '\0';
62
0
    pthread_setname_np(pthread_self(), thread_name);
63
0
  }
64
0
#endif
65
0
  int done = 0;
66
0
  while (!done) {
67
0
    pthread_mutex_lock(&worker->impl_->mutex_);
68
0
    while (worker->status_ == OK) {  // wait in idling mode
69
0
      pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_);
70
0
    }
71
0
    if (worker->status_ == WORK) {
72
0
      execute(worker);
73
0
      worker->status_ = OK;
74
0
    } else if (worker->status_ == NOT_OK) {  // finish the worker
75
0
      done = 1;
76
0
    }
77
    // signal to the main thread that we're done (for sync())
78
0
    pthread_cond_signal(&worker->impl_->condition_);
79
0
    pthread_mutex_unlock(&worker->impl_->mutex_);
80
0
  }
81
0
  return THREAD_RETURN(NULL);  // Thread is finished
82
0
}
83
84
// main thread state control
85
0
static void change_state(AVxWorker *const worker, AVxWorkerStatus new_status) {
86
  // No-op when attempting to change state on a thread that didn't come up.
87
  // Checking status_ without acquiring the lock first would result in a data
88
  // race.
89
0
  if (worker->impl_ == NULL) return;
90
91
0
  pthread_mutex_lock(&worker->impl_->mutex_);
92
0
  if (worker->status_ >= OK) {
93
    // wait for the worker to finish
94
0
    while (worker->status_ != OK) {
95
0
      pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_);
96
0
    }
97
    // assign new status and release the working thread if needed
98
0
    if (new_status != OK) {
99
0
      worker->status_ = new_status;
100
0
      pthread_cond_signal(&worker->impl_->condition_);
101
0
    }
102
0
  }
103
0
  pthread_mutex_unlock(&worker->impl_->mutex_);
104
0
}
105
106
#endif  // CONFIG_MULTITHREAD
107
108
//------------------------------------------------------------------------------
109
110
0
static void init(AVxWorker *const worker) {
111
0
  memset(worker, 0, sizeof(*worker));
112
0
  worker->status_ = NOT_OK;
113
0
}
114
115
0
static int sync(AVxWorker *const worker) {
116
0
#if CONFIG_MULTITHREAD
117
0
  change_state(worker, OK);
118
0
#endif
119
0
  assert(worker->status_ <= OK);
120
0
  return !worker->had_error;
121
0
}
122
123
0
static int reset(AVxWorker *const worker) {
124
0
  int ok = 1;
125
0
  worker->had_error = 0;
126
0
  if (worker->status_ < OK) {
127
0
#if CONFIG_MULTITHREAD
128
0
    worker->impl_ = (AVxWorkerImpl *)aom_calloc(1, sizeof(*worker->impl_));
129
0
    if (worker->impl_ == NULL) {
130
0
      return 0;
131
0
    }
132
0
    if (pthread_mutex_init(&worker->impl_->mutex_, NULL)) {
133
0
      goto Error;
134
0
    }
135
0
    if (pthread_cond_init(&worker->impl_->condition_, NULL)) {
136
0
      pthread_mutex_destroy(&worker->impl_->mutex_);
137
0
      goto Error;
138
0
    }
139
0
    pthread_mutex_lock(&worker->impl_->mutex_);
140
0
    ok = !pthread_create(&worker->impl_->thread_, NULL, thread_loop, worker);
141
0
    if (ok) worker->status_ = OK;
142
0
    pthread_mutex_unlock(&worker->impl_->mutex_);
143
0
    if (!ok) {
144
0
      pthread_mutex_destroy(&worker->impl_->mutex_);
145
0
      pthread_cond_destroy(&worker->impl_->condition_);
146
0
    Error:
147
0
      aom_free(worker->impl_);
148
0
      worker->impl_ = NULL;
149
0
      return 0;
150
0
    }
151
#else
152
    worker->status_ = OK;
153
#endif
154
0
  } else if (worker->status_ > OK) {
155
0
    ok = sync(worker);
156
0
  }
157
0
  assert(!ok || (worker->status_ == OK));
158
0
  return ok;
159
0
}
160
161
0
static void execute(AVxWorker *const worker) {
162
0
  if (worker->hook != NULL) {
163
0
    worker->had_error |= !worker->hook(worker->data1, worker->data2);
164
0
  }
165
0
}
166
167
0
static void launch(AVxWorker *const worker) {
168
0
#if CONFIG_MULTITHREAD
169
0
  change_state(worker, WORK);
170
#else
171
  execute(worker);
172
#endif
173
0
}
174
175
0
static void end(AVxWorker *const worker) {
176
0
#if CONFIG_MULTITHREAD
177
0
  if (worker->impl_ != NULL) {
178
0
    change_state(worker, NOT_OK);
179
0
    pthread_join(worker->impl_->thread_, NULL);
180
0
    pthread_mutex_destroy(&worker->impl_->mutex_);
181
0
    pthread_cond_destroy(&worker->impl_->condition_);
182
0
    aom_free(worker->impl_);
183
0
    worker->impl_ = NULL;
184
0
  }
185
#else
186
  worker->status_ = NOT_OK;
187
  assert(worker->impl_ == NULL);
188
#endif
189
0
  assert(worker->status_ == NOT_OK);
190
0
}
191
192
//------------------------------------------------------------------------------
193
194
static AVxWorkerInterface g_worker_interface = { init,   reset,   sync,
195
                                                 launch, execute, end };
196
197
0
int aom_set_worker_interface(const AVxWorkerInterface *const winterface) {
198
0
  if (winterface == NULL || winterface->init == NULL ||
199
0
      winterface->reset == NULL || winterface->sync == NULL ||
200
0
      winterface->launch == NULL || winterface->execute == NULL ||
201
0
      winterface->end == NULL) {
202
0
    return 0;
203
0
  }
204
0
  g_worker_interface = *winterface;
205
0
  return 1;
206
0
}
207
208
0
const AVxWorkerInterface *aom_get_worker_interface(void) {
209
0
  return &g_worker_interface;
210
0
}
211
212
//------------------------------------------------------------------------------