Coverage Report

Created: 2025-06-13 06:48

/src/libwebp/src/utils/thread_utils.c
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2011 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
// Author: Skal (pascal.massimino@gmail.com)
13
14
#include <assert.h>
15
#include <string.h>   // for memset()
16
17
#include "src/utils/thread_utils.h"
18
#include "src/utils/utils.h"
19
20
#ifdef WEBP_USE_THREAD
21
22
#if defined(_WIN32)
23
24
#include <windows.h>
25
typedef HANDLE pthread_t;
26
typedef CRITICAL_SECTION pthread_mutex_t;
27
28
#if _WIN32_WINNT >= 0x0600  // Windows Vista / Server 2008 or greater
29
#define USE_WINDOWS_CONDITION_VARIABLE
30
typedef CONDITION_VARIABLE pthread_cond_t;
31
#else
32
typedef struct {
33
  HANDLE waiting_sem;
34
  HANDLE received_sem;
35
  HANDLE signal_event;
36
} pthread_cond_t;
37
#endif  // _WIN32_WINNT >= 0x600
38
39
#ifndef WINAPI_FAMILY_PARTITION
40
#define WINAPI_PARTITION_DESKTOP 1
41
#define WINAPI_FAMILY_PARTITION(x) x
42
#endif
43
44
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
45
#define USE_CREATE_THREAD
46
#endif
47
48
#else  // !_WIN32
49
50
#include <pthread.h>
51
52
#endif  // _WIN32
53
54
typedef struct {
55
  pthread_mutex_t mutex;
56
  pthread_cond_t  condition;
57
  pthread_t       thread;
58
} WebPWorkerImpl;
59
60
#if defined(_WIN32)
61
62
//------------------------------------------------------------------------------
63
// simplistic pthread emulation layer
64
65
#include <process.h>
66
67
// _beginthreadex requires __stdcall
68
#define THREADFN unsigned int __stdcall
69
#define THREAD_RETURN(val) (unsigned int)((DWORD_PTR)val)
70
71
#if _WIN32_WINNT >= 0x0501  // Windows XP or greater
72
#define WaitForSingleObject(obj, timeout) \
73
  WaitForSingleObjectEx(obj, timeout, FALSE /*bAlertable*/)
74
#endif
75
76
static int pthread_create(pthread_t* const thread, const void* attr,
77
                          unsigned int (__stdcall* start)(void*), void* arg) {
78
  (void)attr;
79
#ifdef USE_CREATE_THREAD
80
  *thread = CreateThread(NULL,   /* lpThreadAttributes */
81
                         0,      /* dwStackSize */
82
                         start,
83
                         arg,
84
                         0,      /* dwStackSize */
85
                         NULL);  /* lpThreadId */
86
#else
87
  *thread = (pthread_t)_beginthreadex(NULL,   /* void *security */
88
                                      0,      /* unsigned stack_size */
89
                                      start,
90
                                      arg,
91
                                      0,      /* unsigned initflag */
92
                                      NULL);  /* unsigned *thrdaddr */
93
#endif
94
  if (*thread == NULL) return 1;
95
  SetThreadPriority(*thread, THREAD_PRIORITY_ABOVE_NORMAL);
96
  return 0;
97
}
98
99
static int pthread_join(pthread_t thread, void** value_ptr) {
100
  (void)value_ptr;
101
  return (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0 ||
102
          CloseHandle(thread) == 0);
103
}
104
105
// Mutex
106
static int pthread_mutex_init(pthread_mutex_t* const mutex, void* mutexattr) {
107
  (void)mutexattr;
108
#if _WIN32_WINNT >= 0x0600  // Windows Vista / Server 2008 or greater
109
  InitializeCriticalSectionEx(mutex, 0 /*dwSpinCount*/, 0 /*Flags*/);
110
#else
111
  InitializeCriticalSection(mutex);
112
#endif
113
  return 0;
114
}
115
116
static int pthread_mutex_lock(pthread_mutex_t* const mutex) {
117
  EnterCriticalSection(mutex);
118
  return 0;
119
}
120
121
static int pthread_mutex_unlock(pthread_mutex_t* const mutex) {
122
  LeaveCriticalSection(mutex);
123
  return 0;
124
}
125
126
static int pthread_mutex_destroy(pthread_mutex_t* const mutex) {
127
  DeleteCriticalSection(mutex);
128
  return 0;
129
}
130
131
// Condition
132
static int pthread_cond_destroy(pthread_cond_t* const condition) {
133
  int ok = 1;
134
#ifdef USE_WINDOWS_CONDITION_VARIABLE
135
  (void)condition;
136
#else
137
  ok &= (CloseHandle(condition->waiting_sem) != 0);
138
  ok &= (CloseHandle(condition->received_sem) != 0);
139
  ok &= (CloseHandle(condition->signal_event) != 0);
140
#endif
141
  return !ok;
142
}
143
144
static int pthread_cond_init(pthread_cond_t* const condition, void* cond_attr) {
145
  (void)cond_attr;
146
#ifdef USE_WINDOWS_CONDITION_VARIABLE
147
  InitializeConditionVariable(condition);
148
#else
149
  condition->waiting_sem = CreateSemaphore(NULL, 0, 1, NULL);
150
  condition->received_sem = CreateSemaphore(NULL, 0, 1, NULL);
151
  condition->signal_event = CreateEvent(NULL, FALSE, FALSE, NULL);
152
  if (condition->waiting_sem == NULL ||
153
      condition->received_sem == NULL ||
154
      condition->signal_event == NULL) {
155
    pthread_cond_destroy(condition);
156
    return 1;
157
  }
158
#endif
159
  return 0;
160
}
161
162
static int pthread_cond_signal(pthread_cond_t* const condition) {
163
  int ok = 1;
164
#ifdef USE_WINDOWS_CONDITION_VARIABLE
165
  WakeConditionVariable(condition);
166
#else
167
  if (WaitForSingleObject(condition->waiting_sem, 0) == WAIT_OBJECT_0) {
168
    // a thread is waiting in pthread_cond_wait: allow it to be notified
169
    ok = SetEvent(condition->signal_event);
170
    // wait until the event is consumed so the signaler cannot consume
171
    // the event via its own pthread_cond_wait.
172
    ok &= (WaitForSingleObject(condition->received_sem, INFINITE) !=
173
           WAIT_OBJECT_0);
174
  }
175
#endif
176
  return !ok;
177
}
178
179
static int pthread_cond_wait(pthread_cond_t* const condition,
180
                             pthread_mutex_t* const mutex) {
181
  int ok;
182
#ifdef USE_WINDOWS_CONDITION_VARIABLE
183
  ok = SleepConditionVariableCS(condition, mutex, INFINITE);
184
#else
185
  // note that there is a consumer available so the signal isn't dropped in
186
  // pthread_cond_signal
187
  if (!ReleaseSemaphore(condition->waiting_sem, 1, NULL)) return 1;
188
  // now unlock the mutex so pthread_cond_signal may be issued
189
  pthread_mutex_unlock(mutex);
190
  ok = (WaitForSingleObject(condition->signal_event, INFINITE) ==
191
        WAIT_OBJECT_0);
192
  ok &= ReleaseSemaphore(condition->received_sem, 1, NULL);
193
  pthread_mutex_lock(mutex);
194
#endif
195
  return !ok;
196
}
197
198
#else  // !_WIN32
199
# define THREADFN void*
200
0
# define THREAD_RETURN(val) val
201
#endif  // _WIN32
202
203
//------------------------------------------------------------------------------
204
205
0
static THREADFN ThreadLoop(void* ptr) {
206
0
  WebPWorker* const worker = (WebPWorker*)ptr;
207
0
  WebPWorkerImpl* const impl = (WebPWorkerImpl*)worker->impl;
208
0
  int done = 0;
209
0
  while (!done) {
210
0
    pthread_mutex_lock(&impl->mutex);
211
0
    while (worker->status == OK) {   // wait in idling mode
212
0
      pthread_cond_wait(&impl->condition, &impl->mutex);
213
0
    }
214
0
    if (worker->status == WORK) {
215
0
      WebPGetWorkerInterface()->Execute(worker);
216
0
      worker->status = OK;
217
0
    } else if (worker->status == NOT_OK) {   // finish the worker
218
0
      done = 1;
219
0
    }
220
    // signal to the main thread that we're done (for Sync())
221
    // Note the associated mutex does not need to be held when signaling the
222
    // condition. Unlocking the mutex first may improve performance in some
223
    // implementations, avoiding the case where the waiting thread can't
224
    // reacquire the mutex when woken.
225
0
    pthread_mutex_unlock(&impl->mutex);
226
0
    pthread_cond_signal(&impl->condition);
227
0
  }
228
0
  return THREAD_RETURN(NULL);    // Thread is finished
229
0
}
230
231
// main thread state control
232
0
static void ChangeState(WebPWorker* const worker, WebPWorkerStatus new_status) {
233
  // No-op when attempting to change state on a thread that didn't come up.
234
  // Checking 'status' without acquiring the lock first would result in a data
235
  // race.
236
0
  WebPWorkerImpl* const impl = (WebPWorkerImpl*)worker->impl;
237
0
  if (impl == NULL) return;
238
239
0
  pthread_mutex_lock(&impl->mutex);
240
0
  if (worker->status >= OK) {
241
    // wait for the worker to finish
242
0
    while (worker->status != OK) {
243
0
      pthread_cond_wait(&impl->condition, &impl->mutex);
244
0
    }
245
    // assign new status and release the working thread if needed
246
0
    if (new_status != OK) {
247
0
      worker->status = new_status;
248
      // Note the associated mutex does not need to be held when signaling the
249
      // condition. Unlocking the mutex first may improve performance in some
250
      // implementations, avoiding the case where the waiting thread can't
251
      // reacquire the mutex when woken.
252
0
      pthread_mutex_unlock(&impl->mutex);
253
0
      pthread_cond_signal(&impl->condition);
254
0
      return;
255
0
    }
256
0
  }
257
0
  pthread_mutex_unlock(&impl->mutex);
258
0
}
259
260
#endif  // WEBP_USE_THREAD
261
262
//------------------------------------------------------------------------------
263
264
0
static void Init(WebPWorker* const worker) {
265
0
  memset(worker, 0, sizeof(*worker));
266
0
  worker->status = NOT_OK;
267
0
}
268
269
0
static int Sync(WebPWorker* const worker) {
270
0
#ifdef WEBP_USE_THREAD
271
0
  ChangeState(worker, OK);
272
0
#endif
273
0
  assert(worker->status <= OK);
274
0
  return !worker->had_error;
275
0
}
276
277
0
static int Reset(WebPWorker* const worker) {
278
0
  int ok = 1;
279
0
  worker->had_error = 0;
280
0
  if (worker->status < OK) {
281
0
#ifdef WEBP_USE_THREAD
282
0
    WebPWorkerImpl* const impl =
283
0
        (WebPWorkerImpl*)WebPSafeCalloc(1, sizeof(WebPWorkerImpl));
284
0
    worker->impl = (void*)impl;
285
0
    if (worker->impl == NULL) {
286
0
      return 0;
287
0
    }
288
0
    if (pthread_mutex_init(&impl->mutex, NULL)) {
289
0
      goto Error;
290
0
    }
291
0
    if (pthread_cond_init(&impl->condition, NULL)) {
292
0
      pthread_mutex_destroy(&impl->mutex);
293
0
      goto Error;
294
0
    }
295
0
    pthread_mutex_lock(&impl->mutex);
296
0
    ok = !pthread_create(&impl->thread, NULL, ThreadLoop, worker);
297
0
    if (ok) worker->status = OK;
298
0
    pthread_mutex_unlock(&impl->mutex);
299
0
    if (!ok) {
300
0
      pthread_mutex_destroy(&impl->mutex);
301
0
      pthread_cond_destroy(&impl->condition);
302
0
 Error:
303
0
      WebPSafeFree(impl);
304
0
      worker->impl = NULL;
305
0
      return 0;
306
0
    }
307
#else
308
    worker->status = OK;
309
#endif
310
0
  } else if (worker->status > OK) {
311
0
    ok = Sync(worker);
312
0
  }
313
0
  assert(!ok || (worker->status == OK));
314
0
  return ok;
315
0
}
316
317
0
static void Execute(WebPWorker* const worker) {
318
0
  if (worker->hook != NULL) {
319
0
    worker->had_error |= !worker->hook(worker->data1, worker->data2);
320
0
  }
321
0
}
322
323
0
static void Launch(WebPWorker* const worker) {
324
0
#ifdef WEBP_USE_THREAD
325
0
  ChangeState(worker, WORK);
326
#else
327
  Execute(worker);
328
#endif
329
0
}
330
331
0
static void End(WebPWorker* const worker) {
332
0
#ifdef WEBP_USE_THREAD
333
0
  if (worker->impl != NULL) {
334
0
    WebPWorkerImpl* const impl = (WebPWorkerImpl*)worker->impl;
335
0
    ChangeState(worker, NOT_OK);
336
0
    pthread_join(impl->thread, NULL);
337
0
    pthread_mutex_destroy(&impl->mutex);
338
0
    pthread_cond_destroy(&impl->condition);
339
0
    WebPSafeFree(impl);
340
0
    worker->impl = NULL;
341
0
  }
342
#else
343
  worker->status = NOT_OK;
344
  assert(worker->impl == NULL);
345
#endif
346
0
  assert(worker->status == NOT_OK);
347
0
}
348
349
//------------------------------------------------------------------------------
350
351
static WebPWorkerInterface g_worker_interface = {
352
  Init, Reset, Sync, Launch, Execute, End
353
};
354
355
0
int WebPSetWorkerInterface(const WebPWorkerInterface* const winterface) {
356
0
  if (winterface == NULL ||
357
0
      winterface->Init == NULL || winterface->Reset == NULL ||
358
0
      winterface->Sync == NULL || winterface->Launch == NULL ||
359
0
      winterface->Execute == NULL || winterface->End == NULL) {
360
0
    return 0;
361
0
  }
362
0
  g_worker_interface = *winterface;
363
0
  return 1;
364
0
}
365
366
0
const WebPWorkerInterface* WebPGetWorkerInterface(void) {
367
0
  return &g_worker_interface;
368
0
}
369
370
//------------------------------------------------------------------------------