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) Coverage Total Hit
Test: PARI/GP v2.18.1 lcov report (development 31042-0fbe168e69) Lines: 95.3 % 255 243
Test Date: 2026-07-23 17:04:59 Functions: 92.0 % 25 23
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    215121503 : mt_sigint_block(void)
      57              : {
      58    215121503 :   if (mt_thread_no>=0)
      59     35580887 :     pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
      60    215121503 : }
      61              : 
      62              : void
      63    215121501 : mt_sigint_unblock(void)
      64              : {
      65    215121501 :   if (mt_thread_no>=0)
      66     35580887 :     pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
      67    215121488 : }
      68              : 
      69              : void
      70         1870 : mt_err_recover(long er)
      71              : {
      72         1870 :   if (mt_thread_no>=0)
      73              :   {
      74           11 :     struct mt_pstate *mt = pari_mt;
      75           11 :     struct mt_queue *mq = mt->mq+mt_thread_no;
      76           11 :     GEN err = pari_err_last();
      77           11 :     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         1859 :   else mtsingle_err_recover(er);
      87         1859 : }
      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       220894 : mt_is_parallel(void)
     103              : {
     104       220894 :   return !!pari_mt;
     105              : }
     106              : 
     107              : int
     108     31106663 : mt_is_thread(void)
     109              : {
     110     31106663 :   return mt_thread_no>=0 ? 1: mtsingle_is_thread();
     111              : }
     112              : 
     113              : long
     114       389787 : mt_nbthreads(void)
     115              : {
     116       389787 :   return pari_mt ? 1: pari_mt_nbthreads;
     117              : }
     118              : 
     119              : void
     120       376653 : 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          269 : void pari_mt_init(void)
     141              : {
     142          269 :   pari_mt = NULL;
     143              : #ifdef _SC_NPROCESSORS_CONF
     144          269 :   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          269 : }
     151              : 
     152          269 : void pari_mt_close(void) { }
     153              : 
     154              : static void
     155       376653 : mt_queue_cleanup(void *arg)
     156              : {
     157              :   (void) arg;
     158       376653 :   pari_thread_close();
     159       376653 : }
     160              : 
     161              : static void
     162            2 : mt_queue_unlock(void *arg)
     163            2 : { pthread_mutex_unlock((pthread_mutex_t*) arg); }
     164              : 
     165              : static void*
     166       376653 : mt_queue_run(void *arg)
     167              : {
     168       376653 :   GEN args = pari_thread_start((struct pari_thread*) arg);
     169       376653 :   pari_sp av = avma;
     170       376653 :   struct mt_queue *mq = (struct mt_queue *) args;
     171       376653 :   mt_thread_no = mq->no;
     172       376653 :   pthread_cleanup_push(mt_queue_cleanup,NULL);
     173       376653 :   LOCK(mq->pmut)
     174              :   {
     175       376653 :     mq->mainstack = pari_mainstack;
     176       376653 :     mq->avma = av;
     177       376653 :     pthread_cond_signal(mq->pcond);
     178       376653 :   } UNLOCK(mq->pmut);
     179              :   for(;;)
     180       510992 :   {
     181              :     GEN work, done;
     182       887645 :     LOCK(&mq->mut)
     183              :     {
     184       887645 :       pthread_cleanup_push(mt_queue_unlock, &mq->mut);
     185      1751623 :       while(!mq->input)
     186       863980 :         pthread_cond_wait(&mq->cond, &mq->mut);
     187       887643 :       pthread_cleanup_pop(0);
     188       887643 :     } UNLOCK(&mq->mut);
     189       887643 :     pari_mainstack = mq->mainstack;
     190       887643 :     set_avma(mq->avma);
     191       887643 :     work = mq->input;
     192       887643 :     if (typ(work)==t_ERROR && err_get_num(work)==e_STOP) break;
     193       511051 :     pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
     194       511051 :     done = closure_callgenvec(mq->worker,work);
     195       510992 :     pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
     196       510992 :     LOCK(mq->pmut)
     197              :     {
     198       510992 :       mq->mainstack = pari_mainstack;
     199       510992 :       mq->avma = av;
     200       510992 :       mq->input = NULL;
     201       510992 :       mq->output = done;
     202       510992 :       pthread_cond_signal(mq->pcond);
     203       510992 :     } UNLOCK(mq->pmut);
     204              :   }
     205       376592 :   pthread_cleanup_pop(1);
     206              : #ifdef __GNUC__
     207              :   return NULL; /* LCOV_EXCL_LINE */
     208              : #endif
     209              : }
     210              : 
     211              : static long
     212       729538 : mt_queue_check(struct mt_pstate *mt)
     213              : {
     214              :   long i;
     215      5846791 :   for(i=0; i<mt->n; i++)
     216              :   {
     217      5628248 :     struct mt_queue *mq = mt->mq+i;
     218      5628248 :     if (mq->output) return i;
     219              :   }
     220       218543 :   return -1;
     221              : }
     222              : 
     223              : static GEN
     224       815070 : mtpthread_queue_get(struct mt_state *junk, long *workid, long *pending)
     225              : {
     226       815070 :   struct mt_pstate *mt = pari_mt;
     227              :   struct mt_queue *mq;
     228       815070 :   GEN done = NULL;
     229              :   long last;
     230              :   (void) junk;
     231       815070 :   if (mt->nbint<mt->n)
     232              :   {
     233       304073 :     mt->last = mt->nbint;
     234       304073 :     *pending = mt->pending;
     235       304073 :     return NULL;
     236              :   }
     237       510997 :   BLOCK_SIGINT_START
     238       510997 :   LOCK(&mt->pmut)
     239              :   {
     240       729538 :     while ((last = mt_queue_check(mt)) < 0)
     241              :     {
     242       218543 :       pthread_cond_wait(&mt->pcond, &mt->pmut);
     243       218543 :       if (PARI_SIGINT_pending)
     244              :       {
     245            2 :         int sig = PARI_SIGINT_pending;
     246            2 :         PARI_SIGINT_pending = 0;
     247            2 :         pthread_mutex_unlock(&mt->pmut);
     248            2 :         PARI_SIGINT_block = 0;
     249            2 :         raise(sig);
     250            0 :         PARI_SIGINT_block = 1;
     251            0 :         pthread_mutex_lock(&mt->pmut);
     252              :       }
     253              :     }
     254       510995 :   } UNLOCK(&mt->pmut);
     255       510995 :   BLOCK_SIGINT_END
     256       510995 :   mq = mt->mq+last;
     257       510995 :   done = gcopy(mq->output);
     258       510995 :   mq->output = NULL;
     259       510995 :   if (workid) *workid = mq->workid;
     260       510995 :   if (typ(done) == t_ERROR)
     261              :   {
     262            5 :     if (err_get_num(done)==e_STACK)
     263            0 :       pari_err(e_STACKTHREAD);
     264              :     else
     265            5 :       pari_err(0,done);
     266              :   }
     267       510990 :   mt->last = last;
     268       510990 :   mt->pending--;
     269       510990 :   *pending = mt->pending;
     270       510990 :   return done;
     271              : }
     272              : 
     273              : static void
     274       815070 : mtpthread_queue_submit(struct mt_state *junk, long workid, GEN work)
     275              : {
     276       815070 :   struct mt_pstate *mt = pari_mt;
     277       815070 :   struct mt_queue *mq = mt->mq+mt->last;
     278              :   (void) junk;
     279       815070 :   if (!work) { mt->nbint=mt->n; return; }
     280       511051 :   BLOCK_SIGINT_START
     281       511051 :   if (mt->nbint<mt->n)
     282              :   {
     283       369127 :     mt->nbint++;
     284       369127 :     LOCK(mq->pmut)
     285              :     {
     286       446922 :       while(!mq->avma)
     287        77795 :         pthread_cond_wait(mq->pcond, mq->pmut);
     288       369127 :     } UNLOCK(mq->pmut);
     289              :   }
     290       511051 :   LOCK(&mq->mut)
     291              :   {
     292       511051 :     pari_sp av = avma;
     293       511051 :     struct pari_mainstack *st = pari_mainstack;
     294       511051 :     mq->output = NULL;
     295       511051 :     mq->workid = workid;
     296       511051 :     pari_mainstack = mq->mainstack;
     297       511051 :     set_avma(mq->avma);
     298       511051 :     mq->input = gcopy(work);
     299       511051 :     mq->avma = avma;
     300       511051 :     mq->mainstack = pari_mainstack;
     301       511051 :     pari_mainstack = st;
     302       511051 :     set_avma(av);
     303       511051 :     pthread_cond_signal(&mq->cond);
     304       511051 :   } UNLOCK(&mq->mut);
     305       511051 :   mt->pending++;
     306       511051 :   BLOCK_SIGINT_END
     307              : }
     308              : 
     309              : static void
     310        65843 : mtpthread_queue_cleanup(struct mt_pstate *mt)
     311              : {
     312              :   long i;
     313        65843 :   if (DEBUGLEVEL) pari_warn(warner,"stopping %ld threads", mt->n);
     314        65843 :   BLOCK_SIGINT_START
     315        65843 :   pari_mt = NULL;
     316       442496 :   for (i=0;i<mt->n;i++)
     317              :   {
     318       376653 :     struct mt_queue *mq = mt->mq+i;
     319       376653 :     pthread_cond_destroy(&mq->cond);
     320       376653 :     pthread_mutex_destroy(&mq->mut);
     321       376653 :     pari_thread_free(&mt->pth[i]);
     322              :   }
     323        65843 :   pthread_cond_destroy(&mt->pcond);
     324        65843 :   pthread_mutex_destroy(&mt->pmut);
     325        65843 :   BLOCK_SIGINT_END
     326        65843 :   pari_free(mt->mq);
     327        65843 :   pari_free(mt->pth);
     328        65843 :   pari_free(mt->th);
     329        65843 :   pari_free(mt);
     330        65843 : }
     331              : 
     332              : static void
     333        65836 : mtpthread_queue_end(void)
     334              : {
     335        65836 :   const long err_e_STOP[] = { evaltyp(t_ERROR) | _evallg(2), e_STOP};
     336        65836 :   struct mt_pstate *mt = pari_mt;
     337              :   long i;
     338        65836 :   BLOCK_SIGINT_START
     339       442428 :   for (i=0; i<mt->n; i++)
     340              :   {
     341       376592 :     struct mt_queue *mq = mt->mq+i;
     342       376592 :     LOCK(&mq->mut)
     343              :     {
     344       376592 :       mq->output = NULL;
     345       376592 :       mq->workid = 0;
     346       376592 :       mq->input = (GEN) err_e_STOP;
     347       376592 :       pthread_cond_signal(&mq->cond);
     348       376592 :     } UNLOCK(&mq->mut);
     349              :   }
     350       442428 :   for (i=0; i<mt->n; i++)
     351       376592 :     pthread_join(mt->th[i],NULL);
     352        65836 :   mtpthread_queue_cleanup(mt);
     353        65836 :   BLOCK_SIGINT_END
     354        65836 : }
     355              : 
     356              : void
     357            7 : mt_queue_reset(void)
     358              : {
     359            7 :   struct mt_pstate *mt = pari_mt;
     360              :   long i;
     361            7 :   BLOCK_SIGINT_START
     362           68 :   for (i=0; i<mt->n; i++)
     363           61 :     pthread_cancel(mt->th[i]);
     364           68 :   for (i=0; i<mt->n; i++)
     365           61 :     pthread_join(mt->th[i],NULL);
     366            7 :   mtpthread_queue_cleanup(mt);
     367            7 :   BLOCK_SIGINT_END
     368            7 : }
     369              : 
     370              : static long
     371        65843 : closure_has_clone(GEN fun)
     372              : {
     373        65843 :   if (isclone(fun)) return 1;
     374        65837 :   if (lg(fun) >= 8)
     375              :   {
     376        65331 :     GEN f = closure_get_frame(fun);
     377        65331 :     long i, l = lg(f);
     378       241890 :     for (i = 1; i < l; i++)
     379       178219 :       if (isclone(gel(f,i))) return 1;
     380              :   }
     381        64177 :   return 0;
     382              : }
     383              : 
     384              : void
     385       152749 : mt_queue_start_lim(struct pari_mt *pt, GEN worker, long lim)
     386              : {
     387       152749 :   if (lim==0) lim = pari_mt_nbthreads;
     388       152287 :   else        lim = minss(pari_mt_nbthreads, lim);
     389       152749 :   if (mt_thread_no >= 0)
     390        50437 :     mtsequential_queue_start(pt, worker);
     391       102312 :   else if (pari_mt || lim <= 1)
     392        36469 :     mtsingle_queue_start(pt, worker);
     393              :   else
     394              :   {
     395              :     struct mt_pstate *mt =
     396        65843 :            (struct mt_pstate*) pari_malloc(sizeof(struct mt_pstate));
     397        65843 :     long mtparisize = GP_DATA->threadsize? GP_DATA->threadsize: pari_mainstack->rsize;
     398        65843 :     long mtparisizemax = GP_DATA->threadsizemax;
     399              :     long i;
     400        65843 :     if (closure_has_clone(worker))
     401         1666 :       worker = gcopy(worker); /* to avoid clone_lock race */
     402        65843 :     mt->mq  = (struct mt_queue *) pari_malloc(sizeof(*mt->mq)*lim);
     403        65843 :     mt->th  = (pthread_t *) pari_malloc(sizeof(*mt->th)*lim);
     404        65843 :     mt->pth = (struct pari_thread *) pari_malloc(sizeof(*mt->pth)*lim);
     405        65843 :     mt->pending = 0;
     406        65843 :     mt->n = lim;
     407        65843 :     mt->nbint = 0;
     408        65843 :     mt->last = 0;
     409        65843 :     pthread_cond_init(&mt->pcond,NULL);
     410        65843 :     pthread_mutex_init(&mt->pmut,NULL);
     411       442496 :     for (i=0;i<lim;i++)
     412              :     {
     413       376653 :       struct mt_queue *mq = mt->mq+i;
     414       376653 :       mq->no     = i;
     415       376653 :       mq->avma = 0;
     416       376653 :       mq->mainstack = NULL;
     417       376653 :       mq->worker = worker;
     418       376653 :       mq->input  = NULL;
     419       376653 :       mq->output = NULL;
     420       376653 :       mq->pcond  = &mt->pcond;
     421       376653 :       mq->pmut   = &mt->pmut;
     422       376653 :       pthread_cond_init(&mq->cond,NULL);
     423       376653 :       pthread_mutex_init(&mq->mut,NULL);
     424       376653 :       if (mtparisizemax)
     425            0 :         pari_thread_valloc(&mt->pth[i],mtparisize,mtparisizemax,(GEN)mq);
     426              :       else
     427       376653 :         pari_thread_alloc(&mt->pth[i],mtparisize,(GEN)mq);
     428              :     }
     429        65843 :     if (DEBUGLEVEL) pari_warn(warner,"starting %ld threads", lim);
     430        65843 :     BLOCK_SIGINT_START
     431              :     {
     432              :       sigset_t set, oldset;
     433        65843 :       sigfillset(&set);
     434        65843 :       pthread_sigmask(SIG_SETMASK, &set, &oldset);
     435       442496 :       for (i = 0; i < lim; i++)
     436       376653 :         pthread_create(&mt->th[i], NULL, &mt_queue_run, (void*)&mt->pth[i]);
     437        65843 :       pthread_sigmask(SIG_SETMASK, &oldset, NULL);
     438        65843 :       pari_mt = mt;
     439              :     }
     440        65843 :     BLOCK_SIGINT_END
     441        65843 :     pt->get=&mtpthread_queue_get;
     442        65843 :     pt->submit=&mtpthread_queue_submit;
     443        65843 :     pt->end=&mtpthread_queue_end;
     444              :   }
     445       152749 : }
        

Generated by: LCOV version 2.0-1