Coverage Report

Created: 2026-09-14 06:49

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