• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

saitoha / libsixel / 19815657505

01 Dec 2025 08:08AM UTC coverage: 41.258% (-3.8%) from 45.065%
19815657505

push

github

saitoha
python: update shared api.py

10038 of 36877 branches covered (27.22%)

13200 of 31994 relevant lines covered (41.26%)

1167057.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

58.89
/src/sixel_threading.c
1
/*
2
 * SPDX-License-Identifier: MIT
3
 *
4
 * Copyright (c) 2025 libsixel developers. See `AUTHORS`.
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
7
 * this software and associated documentation files (the "Software"), to deal in
8
 * the Software without restriction, including without limitation the rights to
9
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10
 * of the Software, and to permit persons to whom the Software is furnished to do
11
 * so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in all
14
 * copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
 * SOFTWARE.
23
 */
24

25
#include "config.h"
26

27
#if defined(__APPLE__) && !defined(_DARWIN_C_SOURCE)
28
/*
29
 * Expose BSD-flavoured typedefs such as u_int from the macOS SDK when the
30
 * build defines _POSIX_C_SOURCE. The platform headers hide these legacy names
31
 * otherwise, and sys/sysctl.h requires them for data structures like
32
 * struct kinfo_proc.
33
 */
34
# define _DARWIN_C_SOURCE
35
#endif
36

37
#include <stdio.h>
38
#include <stdlib.h>
39
#include <string.h>
40

41
#if HAVE_ERRNO_H
42
# include <errno.h>
43
#endif
44
#if HAVE_LIMITS_H
45
# include <limits.h>
46
#endif
47
#if HAVE_UNISTD_H
48
# include <unistd.h>
49
#endif
50
#if HAVE_SYS_TYPES_H
51
# include <sys/types.h>
52
#endif
53
#if HAVE_SYS_SYSCTL_H
54
# include <sys/sysctl.h>
55
#endif
56

57
#include "sixel_threading.h"
58

59
/*
60
 * Backend selection is performed here so the header remains lightweight.
61
 * WITH_WINPTHREAD forces the pthread path even on Windows to honor
62
 * user-provided configuration switches.
63
 */
64
#if defined(WITH_WINPTHREAD) && WITH_WINPTHREAD
65
# define SIXEL_USE_PTHREADS 1
66
# define SIXEL_USE_WIN32_THREADS 0
67
#elif defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MSYS__)
68
# define SIXEL_USE_PTHREADS 0
69
# define SIXEL_USE_WIN32_THREADS 1
70
#elif SIXEL_ENABLE_THREADS
71
# define SIXEL_USE_PTHREADS 1
72
# define SIXEL_USE_WIN32_THREADS 0
73
#else
74
# define SIXEL_USE_PTHREADS 0
75
# define SIXEL_USE_WIN32_THREADS 0
76
#endif
77

78
#if SIXEL_USE_WIN32_THREADS
79
# include <windows.h>
80
# include <process.h>
81
#endif
82

83
/*
84
 * Provide a thin portability layer for synchronization primitives so the
85
 * encoder can run on POSIX and Windows platforms without altering the public
86
 * API surface.
87
 */
88

89
#if SIXEL_USE_PTHREADS
90

91
/*
92
 * Abort the process when a pthread call fails in a context where recovery is
93
 * impossible. Encoding without locking guarantees would corrupt state, so we
94
 * surface an explicit diagnostic before terminating.
95
 */
96
static void
97
sixel_pthread_abort(const char *what, int error)
×
98
{
99
    fprintf(stderr, "libsixel: %s failed: %d\n", what, error);
×
100
    abort();
×
101
}
102

103
/*
104
 * Entry point passed to pthread_create. It forwards execution to the user
105
 * supplied callback and stores the integer status for later inspection.
106
 */
107
static void *
108
sixel_thread_trampoline(void *arg)
72✔
109
{
110
    sixel_thread_t *thread;
111

112
    thread = (sixel_thread_t *)arg;
72✔
113
    thread->result = thread->fn(thread->arg);
72✔
114
    return NULL;
72✔
115
}
116

117
SIXELAPI int
118
sixel_mutex_init(sixel_mutex_t *mutex)
18✔
119
{
120
    int rc;
121

122
    if (mutex == NULL) {
18!
123
        return SIXEL_BAD_ARGUMENT;
×
124
    }
125
    /*
126
     * Default attributes already provide a non-recursive mutex, which is
127
     * sufficient for the encoder's synchronization requirements.
128
     */
129
    rc = pthread_mutex_init(&mutex->native, NULL);
18✔
130
    if (rc != 0) {
18!
131
        errno = rc;
×
132
        return SIXEL_RUNTIME_ERROR;
×
133
    }
134
    return SIXEL_OK;
18✔
135
}
136

137
SIXELAPI void
138
sixel_mutex_destroy(sixel_mutex_t *mutex)
18✔
139
{
140
    int rc;
141

142
    if (mutex == NULL) {
18!
143
        return;
×
144
    }
145
    rc = pthread_mutex_destroy(&mutex->native);
18✔
146
    if (rc != 0) {
18!
147
        sixel_pthread_abort("pthread_mutex_destroy", rc);
×
148
    }
149
}
150

151
SIXELAPI void
152
sixel_mutex_lock(sixel_mutex_t *mutex)
72✔
153
{
154
    int rc;
155

156
    if (mutex == NULL) {
72!
157
        return;
×
158
    }
159
    rc = pthread_mutex_lock(&mutex->native);
72✔
160
    if (rc != 0) {
72!
161
        sixel_pthread_abort("pthread_mutex_lock", rc);
×
162
    }
163
}
164

165
SIXELAPI void
166
sixel_mutex_unlock(sixel_mutex_t *mutex)
72✔
167
{
168
    int rc;
169

170
    if (mutex == NULL) {
72!
171
        return;
×
172
    }
173
    rc = pthread_mutex_unlock(&mutex->native);
72✔
174
    if (rc != 0) {
72!
175
        sixel_pthread_abort("pthread_mutex_unlock", rc);
×
176
    }
177
}
178

179
SIXELAPI int
180
sixel_cond_init(sixel_cond_t *cond)
18✔
181
{
182
    int rc;
183

184
    if (cond == NULL) {
18!
185
        return SIXEL_BAD_ARGUMENT;
×
186
    }
187
    /*
188
     * Conditions wake waiters in FIFO order per pthreads documentation. No
189
     * custom attributes are needed for the thread pool queue.
190
     */
191
    rc = pthread_cond_init(&cond->native, NULL);
18✔
192
    if (rc != 0) {
18!
193
        errno = rc;
×
194
        return SIXEL_RUNTIME_ERROR;
×
195
    }
196
    return SIXEL_OK;
18✔
197
}
198

199
SIXELAPI void
200
sixel_cond_destroy(sixel_cond_t *cond)
18✔
201
{
202
    int rc;
203

204
    if (cond == NULL) {
18!
205
        return;
×
206
    }
207
    rc = pthread_cond_destroy(&cond->native);
18✔
208
    if (rc != 0) {
18!
209
        sixel_pthread_abort("pthread_cond_destroy", rc);
×
210
    }
211
}
212

213
SIXELAPI void
214
sixel_cond_wait(sixel_cond_t *cond, sixel_mutex_t *mutex)
4✔
215
{
216
    int rc;
217

218
    if (cond == NULL || mutex == NULL) {
4!
219
        return;
×
220
    }
221
    rc = pthread_cond_wait(&cond->native, &mutex->native);
4✔
222
    if (rc != 0) {
4!
223
        sixel_pthread_abort("pthread_cond_wait", rc);
×
224
    }
225
}
226

227
SIXELAPI void
228
sixel_cond_signal(sixel_cond_t *cond)
×
229
{
230
    int rc;
231

232
    if (cond == NULL) {
×
233
        return;
×
234
    }
235
    rc = pthread_cond_signal(&cond->native);
×
236
    if (rc != 0) {
×
237
        sixel_pthread_abort("pthread_cond_signal", rc);
×
238
    }
239
}
240

241
SIXELAPI void
242
sixel_cond_broadcast(sixel_cond_t *cond)
36✔
243
{
244
    int rc;
245

246
    if (cond == NULL) {
36!
247
        return;
×
248
    }
249
    rc = pthread_cond_broadcast(&cond->native);
36✔
250
    if (rc != 0) {
36!
251
        sixel_pthread_abort("pthread_cond_broadcast", rc);
×
252
    }
253
}
254

255
SIXELAPI int
256
sixel_thread_create(sixel_thread_t *thread, sixel_thread_fn fn, void *arg)
72✔
257
{
258
    int rc;
259

260
    if (thread == NULL || fn == NULL) {
72!
261
        return SIXEL_BAD_ARGUMENT;
×
262
    }
263
    /*
264
     * Store context before launching so the trampoline can record the
265
     * callback result inside the same structure without extra allocations.
266
     */
267
    thread->fn = fn;
72✔
268
    thread->arg = arg;
72✔
269
    thread->result = SIXEL_OK;
72✔
270
    thread->started = 0;
72✔
271
    rc = pthread_create(&thread->handle, NULL, sixel_thread_trampoline,
72✔
272
                        thread);
273
    if (rc != 0) {
72!
274
        errno = rc;
×
275
        return SIXEL_RUNTIME_ERROR;
×
276
    }
277
    thread->started = 1;
72✔
278
    return SIXEL_OK;
72✔
279
}
280

281
SIXELAPI void
282
sixel_thread_join(sixel_thread_t *thread)
72✔
283
{
284
    int rc;
285

286
    if (thread == NULL || !thread->started) {
72!
287
        return;
×
288
    }
289
    rc = pthread_join(thread->handle, NULL);
72✔
290
    if (rc != 0) {
72!
291
        sixel_pthread_abort("pthread_join", rc);
×
292
    }
293
    thread->started = 0;
72✔
294
}
295

296
SIXELAPI int
297
sixel_get_hw_threads(void)
×
298
{
299
    long count;
300
#if defined(_SC_NPROCESSORS_ONLN)
301
    count = sysconf(_SC_NPROCESSORS_ONLN);
×
302
    if (count > 0 && count <= (long)INT_MAX) {
×
303
        return (int)count;
×
304
    }
305
#endif
306
#if defined(__APPLE__)
307
    {
308
        int mib[2];
309
        size_t size;
310
        int value;
311

312
        mib[0] = CTL_HW;
313
        mib[1] = HW_AVAILCPU;
314
        size = sizeof(value);
315
        if (sysctl(mib, 2, &value, &size, NULL, 0) == 0 && value > 0) {
×
316
            return value;
317
        }
318
        mib[1] = HW_NCPU;
319
        size = sizeof(value);
320
        if (sysctl(mib, 2, &value, &size, NULL, 0) == 0 && value > 0) {
×
321
            return value;
322
        }
323
    }
324
#endif
325
    return 1;
×
326
}
327

328
#elif SIXEL_USE_WIN32_THREADS
329

330
/*
331
 * Abort execution on unrecoverable Win32 API failures to mirror pthread path
332
 * semantics. Printing the failing call and error code helps debugging in
333
 * environments where stderr is available.
334
 */
335
static void
336
sixel_win32_abort(const char *what, DWORD error)
337
{
338
    fprintf(stderr, "libsixel: %s failed: %lu\n", what,
339
            (unsigned long)error);
340
    abort();
341
}
342

343
/*
344
 * Trampoline for _beginthreadex. It records the callback result back into the
345
 * owning sixel_thread_t structure so the caller can retrieve it after join.
346
 */
347
static unsigned __stdcall
348
sixel_win32_thread_start(void *arg)
349
{
350
    sixel_thread_t *thread;
351

352
    thread = (sixel_thread_t *)arg;
353
    thread->result = thread->fn(thread->arg);
354
    return 0;
355
}
356

357
SIXELAPI int
358
sixel_mutex_init(sixel_mutex_t *mutex)
359
{
360
    if (mutex == NULL) {
361
        return SIXEL_BAD_ARGUMENT;
362
    }
363
    InitializeCriticalSection(&mutex->native);
364
    return SIXEL_OK;
365
}
366

367
SIXELAPI void
368
sixel_mutex_destroy(sixel_mutex_t *mutex)
369
{
370
    if (mutex == NULL) {
371
        return;
372
    }
373
    DeleteCriticalSection(&mutex->native);
374
}
375

376
SIXELAPI void
377
sixel_mutex_lock(sixel_mutex_t *mutex)
378
{
379
    if (mutex == NULL) {
380
        return;
381
    }
382
    EnterCriticalSection(&mutex->native);
383
}
384

385
SIXELAPI void
386
sixel_mutex_unlock(sixel_mutex_t *mutex)
387
{
388
    if (mutex == NULL) {
389
        return;
390
    }
391
    LeaveCriticalSection(&mutex->native);
392
}
393

394
SIXELAPI int
395
sixel_cond_init(sixel_cond_t *cond)
396
{
397
    if (cond == NULL) {
398
        return SIXEL_BAD_ARGUMENT;
399
    }
400
    InitializeConditionVariable(&cond->native);
401
    return SIXEL_OK;
402
}
403

404
SIXELAPI void
405
sixel_cond_destroy(sixel_cond_t *cond)
406
{
407
    /* CONDITION_VARIABLE does not need explicit teardown. */
408
    (void)cond;
409
}
410

411
SIXELAPI void
412
sixel_cond_wait(sixel_cond_t *cond, sixel_mutex_t *mutex)
413
{
414
    BOOL rc;
415
    DWORD error;
416

417
    if (cond == NULL || mutex == NULL) {
418
        return;
419
    }
420
    rc = SleepConditionVariableCS(&cond->native, &mutex->native, INFINITE);
421
    if (rc == 0) {
422
        error = GetLastError();
423
        sixel_win32_abort("SleepConditionVariableCS", error);
424
    }
425
}
426

427
SIXELAPI void
428
sixel_cond_signal(sixel_cond_t *cond)
429
{
430
    if (cond == NULL) {
431
        return;
432
    }
433
    WakeConditionVariable(&cond->native);
434
}
435

436
SIXELAPI void
437
sixel_cond_broadcast(sixel_cond_t *cond)
438
{
439
    if (cond == NULL) {
440
        return;
441
    }
442
    WakeAllConditionVariable(&cond->native);
443
}
444

445
SIXELAPI int
446
sixel_thread_create(sixel_thread_t *thread, sixel_thread_fn fn, void *arg)
447
{
448
    uintptr_t handle;
449

450
    if (thread == NULL || fn == NULL) {
451
        return SIXEL_BAD_ARGUMENT;
452
    }
453
    thread->fn = fn;
454
    thread->arg = arg;
455
    thread->result = SIXEL_OK;
456
    thread->started = 0;
457
    handle = _beginthreadex(NULL, 0, sixel_win32_thread_start, thread, 0,
458
                            NULL);
459
    if (handle == 0) {
460
        return SIXEL_RUNTIME_ERROR;
461
    }
462
    thread->handle = (HANDLE)handle;
463
    thread->started = 1;
464
    return SIXEL_OK;
465
}
466

467
SIXELAPI void
468
sixel_thread_join(sixel_thread_t *thread)
469
{
470
    DWORD rc;
471
    DWORD error;
472

473
    if (thread == NULL || !thread->started) {
474
        return;
475
    }
476
    rc = WaitForSingleObject(thread->handle, INFINITE);
477
    if (rc != WAIT_OBJECT_0) {
478
        error = (rc == WAIT_FAILED) ? GetLastError() : rc;
479
        sixel_win32_abort("WaitForSingleObject", error);
480
    }
481
    CloseHandle(thread->handle);
482
    thread->handle = NULL;
483
    thread->started = 0;
484
}
485

486
SIXELAPI int
487
sixel_get_hw_threads(void)
488
{
489
    DWORD count;
490
    SYSTEM_INFO info;
491

492
    count = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
493
    if (count == 0) {
494
        GetSystemInfo(&info);
495
        count = info.dwNumberOfProcessors;
496
    }
497
    if (count == 0) {
498
        count = 1;
499
    }
500
    return (int)count;
501
}
502

503
#else
504
# error "Thread backend selection failed"
505
#endif /* SIXEL_USE_PTHREADS */
506

507
/* emacs Local Variables:      */
508
/* emacs mode: c               */
509
/* emacs tab-width: 4          */
510
/* emacs indent-tabs-mode: nil */
511
/* emacs c-basic-offset: 4     */
512
/* emacs End:                  */
513
/* vim: set expandtab ts=4 sts=4 sw=4 : */
514
/* EOF */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc