Coverage Report

Created: 2022-08-24 06:15

/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
830
static THREADFN thread_loop(void *ptr) {
42
830
  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
830
  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
828
    char thread_name[16];
60
828
    strncpy(thread_name, worker->thread_name, sizeof(thread_name) - 1);
61
828
    thread_name[sizeof(thread_name) - 1] = '\0';
62
828
    pthread_setname_np(pthread_self(), thread_name);
63
828
  }
64
830
#endif
65
830
  int done = 0;
66
8.64k
  while (!done) {
67
7.81k
    pthread_mutex_lock(&worker->impl_->mutex_);
68
15.6k
    while (worker->status_ == OK) {  // wait in idling mode
69
7.80k
      pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_);
70
7.80k
    }
71
7.81k
    if (worker->status_ == WORK) {
72
6.98k
      execute(worker);
73
6.98k
      worker->status_ = OK;
74
6.98k
    } else if (worker->status_ == NOT_OK) {  // finish the worker
75
830
      done = 1;
76
830
    }
77
    // signal to the main thread that we're done (for sync())
78
7.81k
    pthread_cond_signal(&worker->impl_->condition_);
79
7.81k
    pthread_mutex_unlock(&worker->impl_->mutex_);
80
7.81k
  }
81
830
  return THREAD_RETURN(NULL);  // Thread is finished
82
830
}
83
84
// main thread state control
85
16.2k
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
16.2k
  if (worker->impl_ == NULL) return;
90
91
15.6k
  pthread_mutex_lock(&worker->impl_->mutex_);
92
15.6k
  if (worker->status_ >= OK) {
93
    // wait for the worker to finish
94
16.3k
    while (worker->status_ != OK) {
95
675
      pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_);
96
675
    }
97
    // assign new status and release the working thread if needed
98
15.6k
    if (new_status != OK) {
99
7.81k
      worker->status_ = new_status;
100
7.81k
      pthread_cond_signal(&worker->impl_->condition_);
101
7.81k
    }
102
15.6k
  }
103
15.6k
  pthread_mutex_unlock(&worker->impl_->mutex_);
104
15.6k
}
105
106
#endif  // CONFIG_MULTITHREAD
107
108
//------------------------------------------------------------------------------
109
110
1.45k
static void init(AVxWorker *const worker) {
111
1.45k
  memset(worker, 0, sizeof(*worker));
112
1.45k
  worker->status_ = NOT_OK;
113
1.45k
}
114
115
8.44k
static int sync(AVxWorker *const worker) {
116
8.44k
#if CONFIG_MULTITHREAD
117
8.44k
  change_state(worker, OK);
118
8.44k
#endif
119
8.44k
  assert(worker->status_ <= OK);
120
8.44k
  return !worker->had_error;
121
8.44k
}
122
123
830
static int reset(AVxWorker *const worker) {
124
830
  int ok = 1;
125
830
  worker->had_error = 0;
126
830
  if (worker->status_ < OK) {
127
830
#if CONFIG_MULTITHREAD
128
830
    worker->impl_ = (AVxWorkerImpl *)aom_calloc(1, sizeof(*worker->impl_));
129
830
    if (worker->impl_ == NULL) {
130
0
      return 0;
131
0
    }
132
830
    if (pthread_mutex_init(&worker->impl_->mutex_, NULL)) {
133
0
      goto Error;
134
0
    }
135
830
    if (pthread_cond_init(&worker->impl_->condition_, NULL)) {
136
0
      pthread_mutex_destroy(&worker->impl_->mutex_);
137
0
      goto Error;
138
0
    }
139
830
    pthread_mutex_lock(&worker->impl_->mutex_);
140
830
    ok = !pthread_create(&worker->impl_->thread_, NULL, thread_loop, worker);
141
830
    if (ok) worker->status_ = OK;
142
830
    pthread_mutex_unlock(&worker->impl_->mutex_);
143
830
    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
830
  } else if (worker->status_ > OK) {
155
0
    ok = sync(worker);
156
0
  }
157
830
  assert(!ok || (worker->status_ == OK));
158
830
  return ok;
159
830
}
160
161
12.2k
static void execute(AVxWorker *const worker) {
162
12.2k
  if (worker->hook != NULL) {
163
12.2k
    worker->had_error |= !worker->hook(worker->data1, worker->data2);
164
12.2k
  }
165
12.2k
}
166
167
6.98k
static void launch(AVxWorker *const worker) {
168
6.98k
#if CONFIG_MULTITHREAD
169
6.98k
  change_state(worker, WORK);
170
#else
171
  execute(worker);
172
#endif
173
6.98k
}
174
175
1.45k
static void end(AVxWorker *const worker) {
176
1.45k
#if CONFIG_MULTITHREAD
177
1.45k
  if (worker->impl_ != NULL) {
178
830
    change_state(worker, NOT_OK);
179
830
    pthread_join(worker->impl_->thread_, NULL);
180
830
    pthread_mutex_destroy(&worker->impl_->mutex_);
181
830
    pthread_cond_destroy(&worker->impl_->condition_);
182
830
    aom_free(worker->impl_);
183
830
    worker->impl_ = NULL;
184
830
  }
185
#else
186
  worker->status_ = NOT_OK;
187
  assert(worker->impl_ == NULL);
188
#endif
189
1.45k
  assert(worker->status_ == NOT_OK);
190
1.45k
}
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
7.95k
const AVxWorkerInterface *aom_get_worker_interface(void) {
209
7.95k
  return &g_worker_interface;
210
7.95k
}
211
212
//------------------------------------------------------------------------------