LCOV - code coverage report
Current view: top level - src - thread.c (source / functions) Coverage Total Hit
Test: colopresso Coverage Report Lines: 87.0 % 54 47
Test Date: 2026-11-09 10:50:48 Functions: 100.0 % 5 5
Legend: Lines:     hit not hit

            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              :     bool created;
      28              : } thread_work_t;
      29              : 
      30           47 : static void *thread_worker(void *arg)
      31              : {
      32           47 :     thread_work_t *work = (thread_work_t *)arg;
      33              : 
      34           47 :     if (work && work->func) {
      35           47 :         work->func(work->context, work->start_index, work->end_index);
      36              :     }
      37              : 
      38           47 :     return NULL;
      39              : }
      40              : 
      41           20 : extern uint32_t cpres_get_default_thread_count(void)
      42              : {
      43           20 :     uint32_t cpu_count = colopresso_get_cpu_count(), half = (uint32_t)(cpu_count / 2);
      44              : 
      45           20 :     return half > 0 ? half : 1;
      46              : }
      47              : 
      48           94 : extern uint32_t cpres_get_max_thread_count(void)
      49              : {
      50           94 :     return colopresso_get_cpu_count();
      51              : }
      52              : 
      53            1 : extern bool cpres_is_threads_enabled(void)
      54              : {
      55            1 :     return true;
      56              : }
      57              : 
      58           50 : extern bool colopresso_parallel_for(uint32_t use_threads, uint32_t total_items,
      59              :                                     parallel_func_t func, void *context)
      60              : {
      61              :     colopresso_thread_t *threads;
      62              :     thread_work_t *works;
      63              :     uint32_t thread_count, max_threads, chunk_size, remainder, start, end, i;
      64              :     int rc;
      65              : 
      66           50 :     if (!func || total_items == 0) {
      67            2 :         return false;
      68              :     }
      69              : 
      70           48 :     thread_count = use_threads > 0 ? use_threads : cpres_get_default_thread_count();
      71           48 :     max_threads = cpres_get_max_thread_count();
      72           48 :     if (thread_count > max_threads) {
      73            1 :         thread_count = max_threads;
      74              :     }
      75              : 
      76           48 :     if (thread_count > total_items) {
      77            1 :         thread_count = total_items;
      78              :     }
      79              : 
      80           48 :     if (thread_count <= 1) {
      81           27 :         func(context, 0, total_items);
      82              : 
      83           27 :         return true;
      84              :     }
      85              : 
      86           21 :     threads = (colopresso_thread_t *)malloc(sizeof(colopresso_thread_t) * thread_count);
      87           21 :     works = (thread_work_t *)malloc(sizeof(thread_work_t) * thread_count);
      88           21 :     if (!threads || !works) {
      89            0 :         free(threads);
      90            0 :         free(works);
      91            0 :         func(context, 0, total_items);
      92              : 
      93            0 :         return true;
      94              :     }
      95              : 
      96           21 :     chunk_size = total_items / thread_count;
      97           21 :     remainder = total_items % thread_count;
      98           21 :     start = 0;
      99              : 
     100           68 :     for (i = 0; i < thread_count; ++i) {
     101           47 :         end = start + chunk_size;
     102              : 
     103           47 :         if (i < remainder) {
     104            0 :             ++end;
     105              :         }
     106              : 
     107           47 :         works[i].func = func;
     108           47 :         works[i].context = context;
     109           47 :         works[i].start_index = start;
     110           47 :         works[i].end_index = end;
     111              : 
     112           47 :         rc = colopresso_thread_create(&threads[i], NULL, thread_worker, &works[i]);
     113           47 :         works[i].created = rc == 0;
     114           47 :         if (!works[i].created) {
     115            0 :             colopresso_log(CPRES_LOG_LEVEL_WARNING,
     116              :                            "Threads: colopresso_thread_create failed (rc=%d) - falling back to "
     117              :                            "inline execution",
     118              :                            rc);
     119            0 :             works[i].func(works[i].context, works[i].start_index, works[i].end_index);
     120              :         }
     121              : 
     122           47 :         start = end;
     123              :     }
     124              : 
     125           68 :     for (i = 0; i < thread_count; ++i) {
     126           47 :         if (works[i].created) {
     127           47 :             colopresso_thread_join(threads[i], NULL);
     128              :         }
     129              :     }
     130              : 
     131           21 :     free(threads);
     132           21 :     free(works);
     133              : 
     134           21 :     return true;
     135              : }
     136              : 
     137              : #else
     138              : 
     139              : extern uint32_t cpres_get_default_thread_count(void)
     140              : {
     141              :     return 1;
     142              : }
     143              : 
     144              : extern uint32_t cpres_get_max_thread_count(void)
     145              : {
     146              :     return 1;
     147              : }
     148              : 
     149              : extern bool cpres_is_threads_enabled(void)
     150              : {
     151              :     return false;
     152              : }
     153              : 
     154              : extern bool colopresso_parallel_for(uint32_t use_threads, uint32_t total_items,
     155              :                                     parallel_func_t func, void *context)
     156              : {
     157              :     if (!func || total_items == 0) {
     158              :         return false;
     159              :     }
     160              : 
     161              :     func(context, 0, total_items);
     162              : 
     163              :     return true;
     164              : }
     165              : 
     166              : #endif
        

Generated by: LCOV version 2.0-1