Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-or-later
3 : *
4 : * This file is part of colopresso
5 : *
6 : * Copyright (C) 2025-2026 COLOPL, Inc.
7 : *
8 : * Author: Go Kudo <g-kudo@colopl.co.jp>
9 : * Developed with AI (LLM) code assistance. See `NOTICE` for details.
10 : */
11 :
12 : #include <colopresso.h>
13 : #include <colopresso/portable.h>
14 :
15 : #include "internal/log.h"
16 : #include "internal/threads.h"
17 :
18 : #if COLOPRESSO_ENABLE_THREADS
19 :
20 : #include <stdlib.h>
21 :
22 : typedef struct {
23 : parallel_func_t func;
24 : void *context;
25 : uint32_t start_index;
26 : uint32_t end_index;
27 : } thread_work_t;
28 :
29 47 : static void *thread_worker(void *arg) {
30 47 : thread_work_t *work = (thread_work_t *)arg;
31 :
32 47 : if (work && work->func) {
33 47 : work->func(work->context, work->start_index, work->end_index);
34 : }
35 :
36 47 : return NULL;
37 : }
38 :
39 20 : uint32_t cpres_get_default_thread_count(void) {
40 20 : uint32_t cpu_count = colopresso_get_cpu_count();
41 20 : uint32_t half = (uint32_t)(cpu_count / 2);
42 :
43 20 : return half > 0 ? half : 1;
44 : }
45 :
46 1 : uint32_t cpres_get_max_thread_count(void) { return colopresso_get_cpu_count(); }
47 :
48 1 : bool cpres_is_threads_enabled(void) { return true; }
49 :
50 50 : bool colopresso_parallel_for(uint32_t use_threads, uint32_t total_items, parallel_func_t func, void *context) {
51 : colopresso_thread_t *threads;
52 : thread_work_t *works;
53 : uint32_t thread_count, chunk_size, remainder, start, end, i;
54 : int rc;
55 50 : bool success = true;
56 :
57 50 : if (!func || total_items == 0) {
58 2 : return false;
59 : }
60 :
61 48 : thread_count = use_threads > 0 ? use_threads : cpres_get_default_thread_count();
62 48 : if (thread_count > total_items) {
63 1 : thread_count = total_items;
64 : }
65 :
66 48 : if (thread_count <= 1) {
67 27 : func(context, 0, total_items);
68 27 : return true;
69 : }
70 :
71 21 : threads = (colopresso_thread_t *)malloc(sizeof(colopresso_thread_t) * thread_count);
72 21 : works = (thread_work_t *)malloc(sizeof(thread_work_t) * thread_count);
73 21 : if (!threads || !works) {
74 0 : free(threads);
75 0 : free(works);
76 0 : func(context, 0, total_items);
77 :
78 0 : return true;
79 : }
80 :
81 21 : chunk_size = total_items / thread_count;
82 21 : remainder = total_items % thread_count;
83 21 : start = 0;
84 :
85 68 : for (i = 0; i < thread_count; ++i) {
86 47 : end = start + chunk_size;
87 47 : if (i < remainder) {
88 0 : ++end;
89 : }
90 :
91 47 : works[i].func = func;
92 47 : works[i].context = context;
93 47 : works[i].start_index = start;
94 47 : works[i].end_index = end;
95 :
96 47 : rc = colopresso_thread_create(&threads[i], NULL, thread_worker, &works[i]);
97 47 : if (rc != 0) {
98 0 : colopresso_log(CPRES_LOG_LEVEL_WARNING, "Threads: colopresso_thread_create failed (rc=%d) - falling back to inline execution", rc);
99 0 : works[i].func(works[i].context, works[i].start_index, works[i].end_index);
100 0 : threads[i] = (colopresso_thread_t)NULL;
101 : }
102 :
103 47 : start = end;
104 : }
105 :
106 68 : for (i = 0; i < thread_count; ++i) {
107 47 : if (threads[i] != (colopresso_thread_t)NULL) {
108 47 : colopresso_thread_join(threads[i], NULL);
109 : }
110 : }
111 :
112 21 : free(threads);
113 21 : free(works);
114 :
115 21 : return success;
116 : }
117 :
118 : #else
119 :
120 : uint32_t cpres_get_default_thread_count(void) { return 1; }
121 : uint32_t cpres_get_max_thread_count(void) { return 1; }
122 : bool cpres_is_threads_enabled(void) { return false; }
123 :
124 : bool colopresso_parallel_for(uint32_t use_threads, uint32_t total_items, parallel_func_t func, void *context) {
125 : if (!func || total_items == 0) {
126 : return false;
127 : }
128 :
129 : func(context, 0, total_items);
130 :
131 : return true;
132 : }
133 :
134 : #endif
|