Code coverage tests

This page documents the degree to which the PARI/GP source code is tested by our public test suite, distributed with the source distribution in directory src/test/. This is measured by the gcov utility; we then process gcov output using the lcov frond-end.

We test a few variants depending on Configure flags on the pari.math.u-bordeaux.fr machine (x86_64 architecture), and agregate them in the final report:

The target is to exceed 90% coverage for all mathematical modules (given that branches depending on DEBUGLEVEL or DEBUGMEM are not covered). This script is run to produce the results below.

LCOV - code coverage report
Current view: top level - mt - pthread.c (source / functions) Hit Total Coverage
Test: PARI/GP v2.18.1 lcov report (development 30711-c92306d4b4) Lines: 220 232 94.8 %
Date: 2026-02-24 09:25:14 Functions: 21 23 91.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright (C) 2013  The PARI group.
       2             : 
       3             : This file is part of the PARI/GP package.
       4             : 
       5             : PARI/GP is free software; you can redistribute it and/or modify it under the
       6             : terms of the GNU General Public License as published by the Free Software
       7             : Foundation; either version 2 of the License, or (at your option) any later
       8             : version. It is distributed in the hope that it will be useful, but WITHOUT
       9             : ANY WARRANTY WHATSOEVER.
      10             : 
      11             : Check the License for details. You should have received a copy of it, along
      12             : with the package; see the file 'COPYING'. If not, write to the Free Software
      13             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
      14             : #include <pthread.h>
      15             : #include "pari.h"
      16             : #include "paripriv.h"
      17             : #include "mt.h"
      18             : #if defined(_WIN32)
      19             : #  include "../systems/mingw/mingw.h"
      20             : #endif
      21             : 
      22             : #define DEBUGLEVEL DEBUGLEVEL_mt
      23             : 
      24             : struct mt_queue
      25             : {
      26             :   long no;
      27             :   pari_sp avma;
      28             :   struct pari_mainstack *mainstack;
      29             :   GEN input, output;
      30             :   GEN worker;
      31             :   long workid;
      32             :   pthread_cond_t cond;
      33             :   pthread_mutex_t mut;
      34             :   pthread_cond_t *pcond;
      35             :   pthread_mutex_t *pmut;
      36             : };
      37             : 
      38             : struct mt_pstate
      39             : {
      40             :   pthread_t *th;
      41             :   struct pari_thread *pth;
      42             :   struct mt_queue *mq;
      43             :   long n, nbint, last;
      44             :   long pending;
      45             :   pthread_cond_t pcond;
      46             :   pthread_mutex_t pmut;
      47             : };
      48             : 
      49             : static THREAD long mt_thread_no = -1;
      50             : static struct mt_pstate *pari_mt;
      51             : 
      52             : #define LOCK(x) pthread_mutex_lock(x); do
      53             : #define UNLOCK(x) while(0); pthread_mutex_unlock(x)
      54             : 
      55             : void
      56   208945524 : mt_sigint_block(void)
      57             : {
      58   208945524 :   if (mt_thread_no>=0)
      59    30706416 :     pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
      60   210364189 : }
      61             : 
      62             : void
      63   209340416 : mt_sigint_unblock(void)
      64             : {
      65   209340416 :   if (mt_thread_no>=0)
      66    31088901 :     pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
      67   210395541 : }
      68             : 
      69             : void
      70        1864 : mt_err_recover(long er)
      71             : {
      72        1864 :   if (mt_thread_no>=0)
      73             :   {
      74          10 :     struct mt_pstate *mt = pari_mt;
      75          10 :     struct mt_queue *mq = mt->mq+mt_thread_no;
      76          10 :     GEN err = pari_err_last();
      77          10 :     err = err_get_num(err)==e_STACK ? err_e_STACK: bin_copy(copy_bin(err));
      78          10 :     pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
      79          10 :     LOCK(mq->pmut)
      80             :     {
      81          10 :       mq->output = err;
      82          10 :       pthread_cond_signal(mq->pcond);
      83          10 :     } UNLOCK(mq->pmut);
      84          10 :     pthread_exit((void*)1);
      85             :   }
      86        1854 :   else mtsingle_err_recover(er);
      87        1854 : }
      88             : 
      89             : void
      90           0 : mt_break_recover(void)
      91             : {
      92           0 :   if (mt_thread_no<0) mtsingle_err_recover(0);
      93           0 : }
      94             : 
      95             : void
      96           0 : mt_sigint(void)
      97             : {
      98           0 :   if (pari_mt) pthread_cond_broadcast(&pari_mt->pcond);
      99           0 : }
     100             : 
     101             : int
     102      220708 : mt_is_parallel(void)
     103             : {
     104      220708 :   return !!pari_mt;
     105             : }
     106             : 
     107             : int
     108    31049190 : mt_is_thread(void)
     109             : {
     110    31049190 :   return mt_thread_no>=0 ? 1: mtsingle_is_thread();
     111             : }
     112             : 
     113             : long
     114      389008 : mt_nbthreads(void)
     115             : {
     116      389008 :   return pari_mt ? 1: pari_mt_nbthreads;
     117             : }
     118             : 
     119             : void
     120      369017 : mt_thread_init(void) { mt_thread_no = 0; }
     121             : 
     122             : void
     123          13 : mt_export_add(const char *str, GEN val)
     124             : {
     125          13 :   if (pari_mt)
     126           0 :     pari_err(e_MISC,"export() not allowed during parallel sections");
     127          13 :   export_add(str, val);
     128          13 : }
     129             : 
     130             : void
     131           8 : mt_export_del(const char *str)
     132             : {
     133           8 :   if (pari_mt)
     134           0 :     pari_err(e_MISC,"unexport() not allowed during parallel sections");
     135           8 :   export_del(str);
     136           8 : }
     137             : 
     138           1 : void mt_broadcast(GEN code) {(void) code;}
     139             : 
     140         267 : void pari_mt_init(void)
     141             : {
     142         267 :   pari_mt = NULL;
     143             : #ifdef _SC_NPROCESSORS_CONF
     144         267 :   if (!pari_mt_nbthreads) pari_mt_nbthreads = sysconf(_SC_NPROCESSORS_CONF);
     145             : #elif defined(_WIN32)
     146             :   if (!pari_mt_nbthreads) pari_mt_nbthreads = win32_nbthreads();
     147             : #else
     148             :   pari_mt_nbthreads = 1;
     149             : #endif
     150         267 : }
     151             : 
     152         267 : void pari_mt_close(void) { }
     153             : 
     154             : static void
     155      370582 : mt_queue_cleanup(void *arg)
     156             : {
     157             :   (void) arg;
     158      370582 :   pari_thread_close();
     159      368743 : }
     160             : 
     161             : static void
     162      370112 : mt_queue_unlock(void *arg)
     163      370112 : { pthread_mutex_unlock((pthread_mutex_t*) arg); }
     164             : 
     165             : static void*
     166      371485 : mt_queue_run(void *arg)
     167             : {
     168      371485 :   GEN args = pari_thread_start((struct pari_thread*) arg);
     169      368967 :   pari_sp av = avma;
     170      368967 :   struct mt_queue *mq = (struct mt_queue *) args;
     171      368967 :   mt_thread_no = mq->no;
     172      368967 :   pthread_cleanup_push(mt_queue_cleanup,NULL);
     173      369643 :   LOCK(mq->pmut)
     174             :   {
     175      371513 :     mq->mainstack = pari_mainstack;
     176      371513 :     mq->avma = av;
     177      371513 :     pthread_cond_signal(mq->pcond);
     178      371513 :   } UNLOCK(mq->pmut);
     179             :   for(;;)
     180      502368 :   {
     181             :     GEN work, done;
     182      873862 :     LOCK(&mq->mut)
     183             :     {
     184      873828 :       pthread_cleanup_push(mt_queue_unlock, &mq->mut);
     185     1356612 :       while(!mq->input)
     186      854312 :         pthread_cond_wait(&mq->cond, &mq->mut);
     187      502300 :       pthread_cleanup_pop(0);
     188      502224 :     } UNLOCK(&mq->mut);
     189      502328 :     pari_mainstack = mq->mainstack;
     190      502328 :     set_avma(mq->avma);
     191      502175 :     work = mq->input;
     192      502175 :     pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
     193      502385 :     done = closure_callgenvec(mq->worker,work);
     194      501386 :     pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
     195      502273 :     LOCK(mq->pmut)
     196             :     {
     197      502387 :       mq->mainstack = pari_mainstack;
     198      502387 :       mq->avma = av;
     199      502387 :       mq->input = NULL;
     200      502387 :       mq->output = done;
     201      502387 :       pthread_cond_signal(mq->pcond);
     202      502387 :     } UNLOCK(mq->pmut);
     203             :   }
     204             :   pthread_cleanup_pop(1);
     205             : #ifdef __GNUC__
     206             :   return NULL; /* LCOV_EXCL_LINE */
     207             : #endif
     208             : }
     209             : 
     210             : static long
     211      669740 : mt_queue_check(struct mt_pstate *mt)
     212             : {
     213             :   long i;
     214     4712204 :   for(i=0; i<mt->n; i++)
     215             :   {
     216     4544854 :     struct mt_queue *mq = mt->mq+i;
     217     4544854 :     if (mq->output) return i;
     218             :   }
     219      167350 :   return -1;
     220             : }
     221             : 
     222             : static GEN
     223      801460 : mtpthread_queue_get(struct mt_state *junk, long *workid, long *pending)
     224             : {
     225      801460 :   struct mt_pstate *mt = pari_mt;
     226             :   struct mt_queue *mq;
     227      801460 :   GEN done = NULL;
     228             :   long last;
     229             :   (void) junk;
     230      801460 :   if (mt->nbint<mt->n)
     231             :   {
     232      299068 :     mt->last = mt->nbint;
     233      299068 :     *pending = mt->pending;
     234      299068 :     return NULL;
     235             :   }
     236      502392 :   BLOCK_SIGINT_START
     237      502392 :   LOCK(&mt->pmut)
     238             :   {
     239      669740 :     while ((last = mt_queue_check(mt)) < 0)
     240             :     {
     241      167350 :       pthread_cond_wait(&mt->pcond, &mt->pmut);
     242      167350 :       if (PARI_SIGINT_pending)
     243             :       {
     244           2 :         int sig = PARI_SIGINT_pending;
     245           2 :         PARI_SIGINT_pending = 0;
     246           2 :         pthread_mutex_unlock(&mt->pmut);
     247           2 :         PARI_SIGINT_block = 0;
     248           2 :         raise(sig);
     249           0 :         PARI_SIGINT_block = 1;
     250           0 :         pthread_mutex_lock(&mt->pmut);
     251             :       }
     252             :     }
     253      502390 :   } UNLOCK(&mt->pmut);
     254      502390 :   BLOCK_SIGINT_END
     255      502390 :   mq = mt->mq+last;
     256      502390 :   done = gcopy(mq->output);
     257      502390 :   mq->output = NULL;
     258      502390 :   if (workid) *workid = mq->workid;
     259      502390 :   if (typ(done) == t_ERROR)
     260             :   {
     261           5 :     if (err_get_num(done)==e_STACK)
     262           0 :       pari_err(e_STACKTHREAD);
     263             :     else
     264           5 :       pari_err(0,done);
     265             :   }
     266      502385 :   mt->last = last;
     267      502385 :   mt->pending--;
     268      502385 :   *pending = mt->pending;
     269      502385 :   return done;
     270             : }
     271             : 
     272             : static void
     273      801460 : mtpthread_queue_submit(struct mt_state *junk, long workid, GEN work)
     274             : {
     275      801460 :   struct mt_pstate *mt = pari_mt;
     276      801460 :   struct mt_queue *mq = mt->mq+mt->last;
     277             :   (void) junk;
     278      801460 :   if (!work) { mt->nbint=mt->n; return; }
     279      502446 :   BLOCK_SIGINT_START
     280      502446 :   if (mt->nbint<mt->n)
     281             :   {
     282      363987 :     mt->nbint++;
     283      363987 :     LOCK(mq->pmut)
     284             :     {
     285      452867 :       while(!mq->avma)
     286       88880 :         pthread_cond_wait(mq->pcond, mq->pmut);
     287      363987 :     } UNLOCK(mq->pmut);
     288             :   }
     289      502446 :   LOCK(&mq->mut)
     290             :   {
     291      502446 :     mq->output = NULL;
     292      502446 :     mq->workid = workid;
     293      502446 :     BLOCK_SIGINT_START
     294             :     {
     295      502446 :       pari_sp av = avma;
     296      502446 :       struct pari_mainstack *st = pari_mainstack;
     297      502446 :       pari_mainstack = mq->mainstack;
     298      502446 :       set_avma(mq->avma);
     299      502446 :       mq->input = gcopy(work);
     300      502446 :       mq->avma = avma;
     301      502446 :       mq->mainstack = pari_mainstack;
     302      502446 :       pari_mainstack = st;
     303      502446 :       set_avma(av);
     304             :     }
     305      502446 :     BLOCK_SIGINT_END
     306      502446 :     pthread_cond_signal(&mq->cond);
     307      502446 :   } UNLOCK(&mq->mut);
     308      502446 :   mt->pending++;
     309      502446 :   BLOCK_SIGINT_END
     310             : }
     311             : 
     312             : void
     313       65708 : mt_queue_reset(void)
     314             : {
     315       65708 :   struct mt_pstate *mt = pari_mt;
     316             :   long i;
     317       65708 :   BLOCK_SIGINT_START
     318      437221 :   for (i=0; i<mt->n; i++)
     319      371513 :     pthread_cancel(mt->th[i]);
     320      437221 :   for (i=0; i<mt->n; i++)
     321      371513 :     pthread_join(mt->th[i],NULL);
     322       65708 :   pari_mt = NULL;
     323       65708 :   BLOCK_SIGINT_END
     324       65708 :   if (DEBUGLEVEL) pari_warn(warner,"stopping %ld threads", mt->n);
     325       65708 :   BLOCK_SIGINT_START
     326      437221 :   for (i=0;i<mt->n;i++)
     327             :   {
     328      371513 :     struct mt_queue *mq = mt->mq+i;
     329      371513 :     pthread_cond_destroy(&mq->cond);
     330      371513 :     pthread_mutex_destroy(&mq->mut);
     331      371513 :     pari_thread_free(&mt->pth[i]);
     332             :   }
     333       65708 :   pthread_cond_destroy(&mt->pcond);
     334       65708 :   pthread_mutex_destroy(&mt->pmut);
     335       65708 :   BLOCK_SIGINT_END
     336       65708 :   pari_free(mt->mq);
     337       65708 :   pari_free(mt->pth);
     338       65708 :   pari_free(mt->th);
     339       65708 :   pari_free(mt);
     340       65708 : }
     341             : 
     342             : static long
     343       65708 : closure_has_clone(GEN fun)
     344             : {
     345       65708 :   if (isclone(fun)) return 1;
     346       65702 :   if (lg(fun) >= 8)
     347             :   {
     348       65196 :     GEN f = closure_get_frame(fun);
     349       65196 :     long i, l = lg(f);
     350      240841 :     for (i = 1; i < l; i++)
     351      177287 :       if (isclone(gel(f,i))) return 1;
     352             :   }
     353       64060 :   return 0;
     354             : }
     355             : 
     356             : void
     357      151826 : mt_queue_start_lim(struct pari_mt *pt, GEN worker, long lim)
     358             : {
     359      151826 :   if (lim==0) lim = pari_mt_nbthreads;
     360      151364 :   else        lim = minss(pari_mt_nbthreads, lim);
     361      151827 :   if (mt_thread_no >= 0)
     362       50174 :     mtsequential_queue_start(pt, worker);
     363      101653 :   else if (pari_mt || lim <= 1)
     364       35945 :     mtsingle_queue_start(pt, worker);
     365             :   else
     366             :   {
     367             :     struct mt_pstate *mt =
     368       65708 :            (struct mt_pstate*) pari_malloc(sizeof(struct mt_pstate));
     369       65708 :     long mtparisize = GP_DATA->threadsize? GP_DATA->threadsize: pari_mainstack->rsize;
     370       65708 :     long mtparisizemax = GP_DATA->threadsizemax;
     371             :     long i;
     372       65708 :     if (closure_has_clone(worker))
     373        1648 :       worker = gcopy(worker); /* to avoid clone_lock race */
     374       65708 :     mt->mq  = (struct mt_queue *) pari_malloc(sizeof(*mt->mq)*lim);
     375       65708 :     mt->th  = (pthread_t *) pari_malloc(sizeof(*mt->th)*lim);
     376       65708 :     mt->pth = (struct pari_thread *) pari_malloc(sizeof(*mt->pth)*lim);
     377       65708 :     mt->pending = 0;
     378       65708 :     mt->n = lim;
     379       65708 :     mt->nbint = 0;
     380       65708 :     mt->last = 0;
     381       65708 :     pthread_cond_init(&mt->pcond,NULL);
     382       65708 :     pthread_mutex_init(&mt->pmut,NULL);
     383      437221 :     for (i=0;i<lim;i++)
     384             :     {
     385      371513 :       struct mt_queue *mq = mt->mq+i;
     386      371513 :       mq->no     = i;
     387      371513 :       mq->avma = 0;
     388      371513 :       mq->mainstack = NULL;
     389      371513 :       mq->worker = worker;
     390      371513 :       mq->input  = NULL;
     391      371513 :       mq->output = NULL;
     392      371513 :       mq->pcond  = &mt->pcond;
     393      371513 :       mq->pmut   = &mt->pmut;
     394      371513 :       pthread_cond_init(&mq->cond,NULL);
     395      371513 :       pthread_mutex_init(&mq->mut,NULL);
     396      371513 :       if (mtparisizemax)
     397           0 :         pari_thread_valloc(&mt->pth[i],mtparisize,mtparisizemax,(GEN)mq);
     398             :       else
     399      371513 :         pari_thread_alloc(&mt->pth[i],mtparisize,(GEN)mq);
     400             :     }
     401       65708 :     if (DEBUGLEVEL) pari_warn(warner,"starting %ld threads", lim);
     402       65708 :     BLOCK_SIGINT_START
     403      437221 :     for (i=0;i<lim;i++)
     404      371513 :       pthread_create(&mt->th[i],NULL, &mt_queue_run, (void*)&mt->pth[i]);
     405       65708 :     pari_mt = mt;
     406       65708 :     BLOCK_SIGINT_END
     407       65708 :     pt->get=&mtpthread_queue_get;
     408       65708 :     pt->submit=&mtpthread_queue_submit;
     409       65708 :     pt->end=&mt_queue_reset;
     410             :   }
     411      151827 : }

Generated by: LCOV version 1.16