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.14.0 lcov report (development 26099-280a43a7d3) Lines: 215 224 96.0 %
Date: 2021-01-28 06:10:50 Functions: 20 21 95.2 %
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             : struct mt_queue
      23             : {
      24             :   long no;
      25             :   pari_sp avma;
      26             :   struct pari_mainstack *mainstack;
      27             :   GEN input, output;
      28             :   GEN worker;
      29             :   long workid;
      30             :   pthread_cond_t cond;
      31             :   pthread_mutex_t mut;
      32             :   pthread_cond_t *pcond;
      33             :   pthread_mutex_t *pmut;
      34             : };
      35             : 
      36             : struct mt_pstate
      37             : {
      38             :   pthread_t *th;
      39             :   struct pari_thread *pth;
      40             :   struct mt_queue *mq;
      41             :   long n, nbint, last;
      42             :   long pending;
      43             :   pthread_cond_t pcond;
      44             :   pthread_mutex_t pmut;
      45             : };
      46             : 
      47             : static THREAD long mt_thread_no = -1, mt_issingle = 0;
      48             : static struct mt_pstate *pari_mt;
      49             : 
      50             : #define LOCK(x) pthread_mutex_lock(x); do
      51             : #define UNLOCK(x) while(0); pthread_mutex_unlock(x)
      52             : 
      53             : void
      54   128451488 : mt_sigint_block(void)
      55             : {
      56   128451488 :   if (mt_thread_no>=0)
      57     4618423 :     pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
      58   129845491 : }
      59             : 
      60             : void
      61   128686909 : mt_sigint_unblock(void)
      62             : {
      63   128686909 :   if (mt_thread_no>=0)
      64     4826201 :     pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
      65   129845282 : }
      66             : 
      67             : void
      68        1413 : mt_err_recover(long er)
      69             : {
      70             :   (void) er;
      71        1413 :   if (mt_thread_no>=0)
      72             :   {
      73           7 :     struct mt_pstate *mt = pari_mt;
      74           7 :     struct mt_queue *mq = mt->mq+mt_thread_no;
      75           7 :     GEN err = pari_err_last();
      76           7 :     err = err_get_num(err)==e_STACK ? err_e_STACK: bin_copy(copy_bin(err));
      77           7 :     LOCK(mq->pmut)
      78             :     {
      79           7 :       mq->output = err;
      80           7 :       pthread_cond_signal(mq->pcond);
      81           7 :     } UNLOCK(mq->pmut);
      82           5 :     pthread_exit((void*)1);
      83             :   }
      84        1406 :   else if (mt_issingle) mtsingle_err_recover(er);
      85        1406 : }
      86             : 
      87             : void
      88           0 : mt_sigint(void)
      89             : {
      90           0 :   if (pari_mt) pthread_cond_broadcast(&pari_mt->pcond);
      91           0 : }
      92             : 
      93             : int
      94      204293 : mt_is_parallel(void)
      95             : {
      96      204293 :   return !!pari_mt;
      97             : }
      98             : 
      99             : int
     100    27792155 : mt_is_thread(void)
     101             : {
     102    27792155 :   return mt_thread_no>=0 ? 1: mt_issingle ? mtsingle_is_thread(): 0;
     103             : }
     104             : 
     105             : long
     106       34069 : mt_nbthreads(void)
     107             : {
     108       34069 :   return pari_mt ? 1: pari_mt_nbthreads;
     109             : }
     110             : 
     111             : void
     112          12 : mt_export_add(const char *str, GEN val)
     113             : {
     114          12 :   if (pari_mt)
     115           0 :     pari_err(e_MISC,"export() not allowed during parallel sections");
     116          12 :   export_add(str, val);
     117          12 : }
     118             : 
     119             : void
     120           8 : mt_export_del(const char *str)
     121             : {
     122           8 :   if (pari_mt)
     123           0 :     pari_err(e_MISC,"unexport() not allowed during parallel sections");
     124           8 :   export_del(str);
     125           8 : }
     126             : 
     127           1 : void mt_broadcast(GEN code) {(void) code;}
     128             : 
     129         237 : void pari_mt_init(void)
     130             : {
     131         237 :   pari_mt = NULL;
     132         237 :   mt_issingle = 0;
     133             : #ifdef _SC_NPROCESSORS_CONF
     134         237 :   if (!pari_mt_nbthreads) pari_mt_nbthreads = sysconf(_SC_NPROCESSORS_CONF);
     135             : #elif defined(_WIN32)
     136             :   if (!pari_mt_nbthreads) pari_mt_nbthreads = win32_nbthreads();
     137             : #else
     138             :   pari_mt_nbthreads = 1;
     139             : #endif
     140         237 : }
     141             : 
     142         237 : void pari_mt_close(void) { }
     143             : 
     144             : static void
     145      180261 : mt_queue_cleanup(void *arg)
     146             : {
     147             :   (void) arg;
     148      180261 :   pari_thread_close();
     149      179610 : }
     150             : 
     151             : static void
     152      180234 : mt_queue_unlock(void *arg)
     153      180234 : { pthread_mutex_unlock((pthread_mutex_t*) arg); }
     154             : 
     155             : static void*
     156      181084 : mt_queue_run(void *arg)
     157             : {
     158      181084 :   GEN args = pari_thread_start((struct pari_thread*) arg);
     159      180405 :   pari_sp av = avma;
     160      180405 :   struct mt_queue *mq = (struct mt_queue *) args;
     161      180405 :   mt_thread_no = mq->no;
     162      180405 :   pthread_cleanup_push(mt_queue_cleanup,NULL);
     163      180472 :   LOCK(mq->pmut)
     164             :   {
     165      181163 :     mq->mainstack = pari_mainstack;
     166      181163 :     mq->avma = av;
     167      181163 :     pthread_cond_signal(mq->pcond);
     168      181163 :   } UNLOCK(mq->pmut);
     169             :   for(;;)
     170      204695 :   {
     171             :     GEN work, done;
     172      385841 :     LOCK(&mq->mut)
     173             :     {
     174      385835 :       pthread_cleanup_push(mt_queue_unlock, &mq->mut);
     175      581796 :       while(!mq->input)
     176      377139 :         pthread_cond_wait(&mq->cond, &mq->mut);
     177      204657 :       pthread_cleanup_pop(0);
     178      204645 :     } UNLOCK(&mq->mut);
     179      204741 :     pari_mainstack = mq->mainstack;
     180      204741 :     set_avma(mq->avma);
     181      204624 :     work = mq->input;
     182      204624 :     pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
     183      204718 :     done = closure_callgenvec(mq->worker,work);
     184      204050 :     pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
     185      204616 :     LOCK(mq->pmut)
     186             :     {
     187      204708 :       mq->mainstack = pari_mainstack;
     188      204708 :       mq->avma = av;
     189      204708 :       mq->input = NULL;
     190      204708 :       mq->output = done;
     191      204708 :       pthread_cond_signal(mq->pcond);
     192      204708 :     } UNLOCK(mq->pmut);
     193             :   }
     194             :   pthread_cleanup_pop(1);
     195             : #ifdef __GNUC__
     196             :   return NULL; /* LCOV_EXCL_LINE */
     197             : #endif
     198             : }
     199             : 
     200             : static long
     201      289993 : mt_queue_check(struct mt_pstate *mt)
     202             : {
     203             :   long i;
     204     3317512 :   for(i=0; i<mt->n; i++)
     205             :   {
     206     3232230 :     struct mt_queue *mq = mt->mq+i;
     207     3232230 :     if (mq->output) return i;
     208             :   }
     209       85282 :   return -1;
     210             : }
     211             : 
     212             : static GEN
     213      363617 : mtpthread_queue_get(struct mt_state *junk, long *workid, long *pending)
     214             : {
     215      363617 :   struct mt_pstate *mt = pari_mt;
     216             :   struct mt_queue *mq;
     217      363617 :   GEN done = NULL;
     218             :   long last;
     219             :   (void) junk;
     220      363617 :   if (mt->nbint<mt->n)
     221             :   {
     222      158904 :     mt->last = mt->nbint;
     223      158904 :     *pending = mt->pending;
     224      158904 :     return NULL;
     225             :   }
     226      204713 :   BLOCK_SIGINT_START
     227      204713 :   LOCK(&mt->pmut)
     228             :   {
     229      289993 :     while ((last = mt_queue_check(mt)) < 0)
     230             :     {
     231       85282 :       pthread_cond_wait(&mt->pcond, &mt->pmut);
     232       85282 :       if (PARI_SIGINT_pending)
     233             :       {
     234           2 :         int sig = PARI_SIGINT_pending;
     235           2 :         PARI_SIGINT_pending = 0;
     236           2 :         pthread_mutex_unlock(&mt->pmut);
     237           2 :         PARI_SIGINT_block = 0;
     238           2 :         raise(sig);
     239           0 :         PARI_SIGINT_block = 1;
     240           0 :         pthread_mutex_lock(&mt->pmut);
     241             :       }
     242             :     }
     243      204711 :   } UNLOCK(&mt->pmut);
     244      204711 :   BLOCK_SIGINT_END
     245      204711 :   mq = mt->mq+last;
     246      204711 :   done = gcopy(mq->output);
     247      204711 :   mq->output = NULL;
     248      204711 :   if (workid) *workid = mq->workid;
     249      204711 :   if (typ(done) == t_ERROR)
     250             :   {
     251           5 :     if (err_get_num(done)==e_STACK)
     252           0 :       pari_err(e_STACKTHREAD);
     253             :     else
     254           5 :       pari_err(0,done);
     255             :   }
     256      204706 :   mt->last = last;
     257      204706 :   mt->pending--;
     258      204706 :   *pending = mt->pending;
     259      204706 :   return done;
     260             : }
     261             : 
     262             : static void
     263      363617 : mtpthread_queue_submit(struct mt_state *junk, long workid, GEN work)
     264             : {
     265      363617 :   struct mt_pstate *mt = pari_mt;
     266      363617 :   struct mt_queue *mq = mt->mq+mt->last;
     267             :   (void) junk;
     268      363617 :   if (!work) { mt->nbint=mt->n; return; }
     269      204799 :   BLOCK_SIGINT_START
     270      204799 :   if (mt->nbint<mt->n)
     271             :   {
     272      180517 :     mt->nbint++;
     273      180517 :     LOCK(mq->pmut)
     274             :     {
     275      211232 :       while(!mq->avma)
     276       30715 :         pthread_cond_wait(mq->pcond, mq->pmut);
     277      180517 :     } UNLOCK(mq->pmut);
     278             :   }
     279      204799 :   LOCK(&mq->mut)
     280             :   {
     281      204799 :     mq->output = NULL;
     282      204799 :     mq->workid = workid;
     283      204799 :     BLOCK_SIGINT_START
     284             :     {
     285      204799 :       pari_sp av = avma;
     286      204799 :       struct pari_mainstack *st = pari_mainstack;
     287      204799 :       pari_mainstack = mq->mainstack;
     288      204799 :       set_avma(mq->avma);
     289      204799 :       mq->input = gcopy(work);
     290      204799 :       mq->avma = avma;
     291      204799 :       mq->mainstack = pari_mainstack;
     292      204799 :       pari_mainstack = st;
     293      204799 :       set_avma(av);
     294             :     }
     295      204799 :     BLOCK_SIGINT_END
     296      204799 :     pthread_cond_signal(&mq->cond);
     297      204799 :   } UNLOCK(&mq->mut);
     298      204799 :   mt->pending++;
     299      204799 :   BLOCK_SIGINT_END
     300             : }
     301             : 
     302             : void
     303       21959 : mt_queue_reset(void)
     304             : {
     305       21959 :   struct mt_pstate *mt = pari_mt;
     306             :   long i;
     307       21959 :   BLOCK_SIGINT_START
     308      203122 :   for (i=0; i<mt->n; i++)
     309      181163 :     pthread_cancel(mt->th[i]);
     310      203122 :   for (i=0; i<mt->n; i++)
     311      181163 :     pthread_join(mt->th[i],NULL);
     312       21959 :   pari_mt = NULL;
     313       21959 :   BLOCK_SIGINT_END
     314       21959 :   if (DEBUGLEVEL) pari_warn(warner,"stopping %ld threads", mt->n);
     315      203122 :   for (i=0;i<mt->n;i++)
     316             :   {
     317      181163 :     struct mt_queue *mq = mt->mq+i;
     318      181163 :     pthread_cond_destroy(&mq->cond);
     319      181163 :     pthread_mutex_destroy(&mq->mut);
     320      181163 :     pari_thread_free(&mt->pth[i]);
     321             :   }
     322       21959 :   pari_free(mt->mq);
     323       21959 :   pari_free(mt->pth);
     324       21959 :   pari_free(mt->th);
     325       21959 :   pari_free(mt);
     326       21959 : }
     327             : 
     328             : static long
     329       21959 : closure_has_clone(GEN fun)
     330             : {
     331       21959 :   if (isclone(fun)) return 1;
     332       21957 :   if (lg(fun) >= 8)
     333             :   {
     334       21467 :     GEN f = closure_get_frame(fun);
     335       21467 :     long i, l = lg(f);
     336       82927 :     for (i = 1; i < l; i++)
     337       62376 :       if (isclone(gel(f,i))) return 1;
     338             :   }
     339       21041 :   return 0;
     340             : }
     341             : 
     342             : void
     343       29284 : mt_queue_start_lim(struct pari_mt *pt, GEN worker, long lim)
     344             : {
     345       29284 :   if (lim==0) lim = pari_mt_nbthreads;
     346       29265 :   else        lim = minss(pari_mt_nbthreads, lim);
     347       29284 :   if (pari_mt || lim <= 1)
     348             :   {
     349        7325 :     mt_issingle = 1;
     350        7325 :     mtsingle_queue_start(pt, worker);
     351             :   }
     352             :   else
     353             :   {
     354             :     struct mt_pstate *mt =
     355       21959 :            (struct mt_pstate*) pari_malloc(sizeof(struct mt_pstate));
     356       21959 :     long mtparisize = GP_DATA->threadsize? GP_DATA->threadsize: pari_mainstack->rsize;
     357       21959 :     long mtparisizemax = GP_DATA->threadsizemax;
     358             :     long i;
     359       21959 :     if (closure_has_clone(worker))
     360         918 :       worker = gcopy(worker); /* to avoid clone_lock race */
     361       21959 :     mt->mq  = (struct mt_queue *) pari_malloc(sizeof(*mt->mq)*lim);
     362       21959 :     mt->th  = (pthread_t *) pari_malloc(sizeof(*mt->th)*lim);
     363       21959 :     mt->pth = (struct pari_thread *) pari_malloc(sizeof(*mt->pth)*lim);
     364       21959 :     mt->pending = 0;
     365       21959 :     mt->n = lim;
     366       21959 :     mt->nbint = 0;
     367       21959 :     mt->last = 0;
     368       21959 :     pthread_cond_init(&mt->pcond,NULL);
     369       21959 :     pthread_mutex_init(&mt->pmut,NULL);
     370      203122 :     for (i=0;i<lim;i++)
     371             :     {
     372      181163 :       struct mt_queue *mq = mt->mq+i;
     373      181163 :       mq->no     = i;
     374      181163 :       mq->avma = 0;
     375      181163 :       mq->mainstack = NULL;
     376      181163 :       mq->worker = worker;
     377      181163 :       mq->input  = NULL;
     378      181163 :       mq->output = NULL;
     379      181163 :       mq->pcond  = &mt->pcond;
     380      181163 :       mq->pmut   = &mt->pmut;
     381      181163 :       pthread_cond_init(&mq->cond,NULL);
     382      181163 :       pthread_mutex_init(&mq->mut,NULL);
     383      181163 :       if (mtparisizemax)
     384           0 :         pari_thread_valloc(&mt->pth[i],mtparisize,mtparisizemax,(GEN)mq);
     385             :       else
     386      181163 :         pari_thread_alloc(&mt->pth[i],mtparisize,(GEN)mq);
     387             :     }
     388       21959 :     if (DEBUGLEVEL) pari_warn(warner,"starting %ld threads", lim);
     389       21959 :     BLOCK_SIGINT_START
     390      203122 :     for (i=0;i<lim;i++)
     391      181163 :       pthread_create(&mt->th[i],NULL, &mt_queue_run, (void*)&mt->pth[i]);
     392       21959 :     pari_mt = mt;
     393       21959 :     mt_issingle = 0;
     394       21959 :     BLOCK_SIGINT_END
     395       21959 :     pt->get=&mtpthread_queue_get;
     396       21959 :     pt->submit=&mtpthread_queue_submit;
     397       21959 :     pt->end=&mt_queue_reset;
     398             :   }
     399       29284 : }

Generated by: LCOV version 1.13