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.12.1 lcov report (development 24038-ebe36f6c4) Lines: 203 214 94.9 %
Date: 2019-07-23 05:53:17 Functions: 17 19 89.5 %
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. It is distributed in the hope that it will be useful, but WITHOUT
       8             : ANY WARRANTY WHATSOEVER.
       9             : 
      10             : Check the License for details. You should have received a copy of it, along
      11             : with the package; see the file 'COPYING'. If not, write to the Free Software
      12             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
      13             : #include <pthread.h>
      14             : #include "pari.h"
      15             : #include "paripriv.h"
      16             : #include "mt.h"
      17             : #if defined(_WIN32)
      18             : #  include "../systems/mingw/mingw.h"
      19             : #endif
      20             : 
      21             : struct mt_queue
      22             : {
      23             :   long no;
      24             :   pari_sp avma;
      25             :   struct pari_mainstack *mainstack;
      26             :   GEN input, output;
      27             :   GEN worker;
      28             :   long workid;
      29             :   pthread_cond_t cond;
      30             :   pthread_mutex_t mut;
      31             :   pthread_cond_t *pcond;
      32             :   pthread_mutex_t *pmut;
      33             : };
      34             : 
      35             : struct mt_pstate
      36             : {
      37             :   pthread_t *th;
      38             :   struct pari_thread *pth;
      39             :   struct mt_queue *mq;
      40             :   long n, nbint, last;
      41             :   long pending;
      42             :   pthread_cond_t pcond;
      43             :   pthread_mutex_t pmut;
      44             : };
      45             : 
      46             : static THREAD long mt_thread_no = -1;
      47             : static struct mt_pstate *pari_mt;
      48             : 
      49             : #define LOCK(x) pthread_mutex_lock(x); do
      50             : #define UNLOCK(x) while(0); pthread_mutex_unlock(x)
      51             : 
      52             : void
      53   136341101 : mt_sigint_block(void)
      54             : {
      55   136341101 :   if (mt_thread_no>=0)
      56     5832161 :     pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
      57   138521995 : }
      58             : 
      59             : void
      60   137288806 : mt_sigint_unblock(void)
      61             : {
      62   137288806 :   if (mt_thread_no>=0)
      63     6740853 :     pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
      64   138378717 : }
      65             : 
      66             : void
      67        1332 : mt_err_recover(long er)
      68             : {
      69             :   (void) er;
      70        1332 :   if (mt_thread_no>=0)
      71             :   {
      72           2 :     struct mt_pstate *mt = pari_mt;
      73           2 :     struct mt_queue *mq = mt->mq+mt_thread_no;
      74           2 :     GEN err = pari_err_last();
      75           2 :     err = err_get_num(err)==e_STACK ? err_e_STACK: bin_copy(copy_bin(err));
      76           2 :     LOCK(mq->pmut)
      77             :     {
      78           2 :       mq->output = err;
      79           2 :       pthread_cond_signal(mq->pcond);
      80           2 :     } UNLOCK(mq->pmut);
      81           0 :     pthread_exit((void*)1);
      82             :   }
      83        1330 : }
      84             : 
      85             : void
      86           0 : mt_sigint(void)
      87             : {
      88           0 :   if (pari_mt) pthread_cond_broadcast(&pari_mt->pcond);
      89           0 : }
      90             : 
      91             : int
      92      202878 : mt_is_parallel(void)
      93             : {
      94      202878 :   return !!pari_mt;
      95             : }
      96             : 
      97             : int
      98    27792498 : mt_is_thread(void)
      99             : {
     100    27792498 :   return mt_thread_no>=0;
     101             : }
     102             : 
     103             : void
     104           8 : mt_export_add(const char *str, GEN val)
     105             : {
     106           8 :   if (pari_mt)
     107           0 :     pari_err(e_MISC,"export() not allowed during parallel sections");
     108           8 :   export_add(str, val);
     109           8 : }
     110             : 
     111             : void
     112           8 : mt_export_del(const char *str)
     113             : {
     114           8 :   if (pari_mt)
     115           0 :     pari_err(e_MISC,"unexport() not allowed during parallel sections");
     116           8 :   export_del(str);
     117           8 : }
     118             : 
     119           0 : void mt_broadcast(GEN code) {(void) code;}
     120             : 
     121         226 : void pari_mt_init(void)
     122             : {
     123         226 :   pari_mt = NULL;
     124             : #ifdef _SC_NPROCESSORS_CONF
     125         226 :   if (!pari_mt_nbthreads) pari_mt_nbthreads = sysconf(_SC_NPROCESSORS_CONF);
     126             : #elif defined(_WIN32)
     127             :   if (!pari_mt_nbthreads) pari_mt_nbthreads = win32_nbthreads();
     128             : #else
     129             :   pari_mt_nbthreads = 1;
     130             : #endif
     131         226 : }
     132             : 
     133         226 : void pari_mt_close(void) { }
     134             : 
     135             : static void
     136      424985 : mt_queue_cleanup(void *arg)
     137             : {
     138             :   (void) arg;
     139      424985 :   pari_thread_close();
     140      424334 : }
     141             : 
     142             : static void*
     143      423740 : mt_queue_run(void *arg)
     144             : {
     145      423740 :   GEN args = pari_thread_start((struct pari_thread*) arg);
     146      424519 :   pari_sp av = avma;
     147      424519 :   struct mt_queue *mq = (struct mt_queue *) args;
     148      424519 :   mt_thread_no = mq->no;
     149      424519 :   pthread_cleanup_push(mt_queue_cleanup,NULL);
     150      425059 :   LOCK(mq->pmut)
     151             :   {
     152      425963 :     mq->mainstack = pari_mainstack;
     153      425963 :     mq->avma = av;
     154      425963 :     pthread_cond_signal(mq->pcond);
     155      425963 :   } UNLOCK(mq->pmut);
     156             :   for(;;)
     157      193287 :   {
     158             :     GEN work, done;
     159      619246 :     LOCK(&mq->mut)
     160             :     {
     161     1417503 :       while(!mq->input)
     162      605059 :         pthread_cond_wait(&mq->cond, &mq->mut);
     163      193288 :     } UNLOCK(&mq->mut);
     164      193320 :     pari_mainstack = mq->mainstack;
     165      193320 :     set_avma(mq->avma);
     166      193197 :     work = mq->input;
     167      193197 :     pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
     168      193390 :     done = closure_callgenvec(mq->worker,work);
     169      187944 :     pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
     170      192440 :     LOCK(mq->pmut)
     171             :     {
     172      193367 :       mq->mainstack = pari_mainstack;
     173      193367 :       mq->avma = av;
     174      193367 :       mq->input = NULL;
     175      193367 :       mq->output = done;
     176      193367 :       pthread_cond_signal(mq->pcond);
     177      193367 :     } UNLOCK(mq->pmut);
     178             :   }
     179             :   pthread_cleanup_pop(1);
     180             : #ifdef __GNUC__
     181             :   return NULL; /* LCOV_EXCL_LINE */
     182             : #endif
     183             : }
     184             : 
     185             : static long
     186      304140 : mt_queue_check(struct mt_pstate *mt)
     187             : {
     188             :   long i;
     189     3672356 :   for(i=0; i<mt->n; i++)
     190             :   {
     191     3561582 :     struct mt_queue *mq = mt->mq+i;
     192     3561582 :     if (mq->output) return i;
     193             :   }
     194      110774 :   return -1;
     195             : }
     196             : 
     197             : static GEN
     198      317124 : mtpthread_queue_get(struct mt_state *junk, long *workid, long *pending)
     199             : {
     200      317124 :   struct mt_pstate *mt = pari_mt;
     201             :   struct mt_queue *mq;
     202      317124 :   GEN done = NULL;
     203             :   long last;
     204             :   (void) junk;
     205      317124 :   if (mt->nbint<mt->n)
     206             :   {
     207      123756 :     mt->last = mt->nbint;
     208      123756 :     *pending = mt->pending;
     209      123756 :     return NULL;
     210             :   }
     211      193368 :   BLOCK_SIGINT_START
     212      193368 :   LOCK(&mt->pmut)
     213             :   {
     214      497508 :     while ((last = mt_queue_check(mt)) < 0)
     215             :     {
     216      110774 :       pthread_cond_wait(&mt->pcond, &mt->pmut);
     217      110774 :       if (PARI_SIGINT_pending)
     218             :       {
     219           2 :         int sig = PARI_SIGINT_pending;
     220           2 :         PARI_SIGINT_pending = 0;
     221           2 :         pthread_mutex_unlock(&mt->pmut);
     222           2 :         PARI_SIGINT_block = 0;
     223           2 :         raise(sig);
     224           0 :         PARI_SIGINT_block = 1;
     225           0 :         pthread_mutex_lock(&mt->pmut);
     226             :       }
     227             :     }
     228      193366 :   } UNLOCK(&mt->pmut);
     229      193366 :   BLOCK_SIGINT_END
     230      193366 :   mq = mt->mq+last;
     231      193366 :   done = gcopy(mq->output);
     232      193366 :   mq->output = NULL;
     233      193366 :   if (workid) *workid = mq->workid;
     234      193366 :   if (typ(done) == t_ERROR)
     235             :   {
     236           2 :     if (err_get_num(done)==e_STACK)
     237           0 :       pari_err(e_STACKTHREAD);
     238             :     else
     239           2 :       pari_err(0,done);
     240             :   }
     241      193364 :   mt->last = last;
     242      193364 :   mt->pending--;
     243      193364 :   *pending = mt->pending;
     244      193364 :   return done;
     245             : }
     246             : 
     247             : static void
     248      317124 : mtpthread_queue_submit(struct mt_state *junk, long workid, GEN work)
     249             : {
     250      317124 :   struct mt_pstate *mt = pari_mt;
     251      317124 :   struct mt_queue *mq = mt->mq+mt->last;
     252             :   (void) junk;
     253      317124 :   if (!work) { mt->nbint=mt->n; return; }
     254      193448 :   BLOCK_SIGINT_START
     255      193448 :   if (mt->nbint<mt->n)
     256             :   {
     257      149809 :     mt->nbint++;
     258      149809 :     LOCK(mq->pmut)
     259             :     {
     260      456161 :       while(!mq->avma)
     261      156543 :         pthread_cond_wait(mq->pcond, mq->pmut);
     262      149809 :     } UNLOCK(mq->pmut);
     263             :   }
     264      193448 :   LOCK(&mq->mut)
     265             :   {
     266      193448 :     mq->output = NULL;
     267      193448 :     mq->workid = workid;
     268      193448 :     BLOCK_SIGINT_START
     269             :     {
     270      193448 :       pari_sp av = avma;
     271      193448 :       struct pari_mainstack *st = pari_mainstack;
     272      193448 :       pari_mainstack = mq->mainstack;
     273      193448 :       set_avma(mq->avma);
     274      193448 :       mq->input = gcopy(work);
     275      193448 :       mq->avma = avma;
     276      193448 :       mq->mainstack = pari_mainstack;
     277      193448 :       pari_mainstack = st;
     278      193448 :       set_avma(av);
     279             :     }
     280      193448 :     BLOCK_SIGINT_END
     281      193448 :     pthread_cond_signal(&mq->cond);
     282      193448 :   } UNLOCK(&mq->mut);
     283      193448 :   mt->pending++;
     284      193448 :   BLOCK_SIGINT_END
     285             : }
     286             : 
     287             : void
     288       34999 : mt_queue_reset(void)
     289             : {
     290       34999 :   struct mt_pstate *mt = pari_mt;
     291             :   long i;
     292       34999 :   BLOCK_SIGINT_START
     293      460962 :   for (i=0; i<mt->n; i++)
     294      425963 :     pthread_cancel(mt->th[i]);
     295      460962 :   for (i=0; i<mt->n; i++)
     296      425963 :     pthread_join(mt->th[i],NULL);
     297       34999 :   pari_mt = NULL;
     298       34999 :   BLOCK_SIGINT_END
     299       34999 :   if (DEBUGLEVEL) pari_warn(warner,"stopping %ld threads", mt->n);
     300      460962 :   for (i=0;i<mt->n;i++)
     301             :   {
     302      425963 :     struct mt_queue *mq = mt->mq+i;
     303      425963 :     pthread_cond_destroy(&mq->cond);
     304      425963 :     pthread_mutex_destroy(&mq->mut);
     305      425963 :     pari_thread_free(&mt->pth[i]);
     306             :   }
     307       34999 :   pari_free(mt->mq);
     308       34999 :   pari_free(mt->pth);
     309       34999 :   pari_free(mt->th);
     310       34999 :   pari_free(mt);
     311       34999 : }
     312             : 
     313             : static long
     314       34999 : closure_has_clone(GEN fun)
     315             : {
     316       34999 :   if (isclone(fun)) return 1;
     317       34997 :   if (lg(fun) >= 8)
     318             :   {
     319       34993 :     GEN f = closure_get_frame(fun);
     320       34993 :     long i, l = lg(f);
     321      140239 :     for (i = 1; i < l; i++)
     322      105446 :       if (isclone(gel(f,i))) return 1;
     323             :   }
     324       34797 :   return 0;
     325             : }
     326             : 
     327             : void
     328       40513 : mt_queue_start_lim(struct pari_mt *pt, GEN worker, long lim)
     329             : {
     330       40513 :   if (lim==0) lim = pari_mt_nbthreads;
     331       31879 :   else        lim = minss(pari_mt_nbthreads, lim);
     332       40514 :   if (pari_mt || lim <= 1)
     333        5515 :     mtsingle_queue_start(pt, worker);
     334             :   else
     335             :   {
     336       34999 :     struct mt_pstate *mt =
     337             :            (struct mt_pstate*) pari_malloc(sizeof(struct mt_pstate));
     338       34999 :     long mtparisize = GP_DATA->threadsize? GP_DATA->threadsize: pari_mainstack->rsize;
     339       34999 :     long mtparisizemax = GP_DATA->threadsizemax;
     340             :     long i;
     341       34999 :     if (closure_has_clone(worker))
     342         202 :       worker = gcopy(worker); /* to avoid clone_lock race */
     343       34999 :     mt->mq  = (struct mt_queue *) pari_malloc(sizeof(*mt->mq)*lim);
     344       34999 :     mt->th  = (pthread_t *) pari_malloc(sizeof(*mt->th)*lim);
     345       34999 :     mt->pth = (struct pari_thread *) pari_malloc(sizeof(*mt->pth)*lim);
     346       34999 :     mt->pending = 0;
     347       34999 :     mt->n = lim;
     348       34999 :     mt->nbint = 0;
     349       34999 :     mt->last = 0;
     350       34999 :     pthread_cond_init(&mt->pcond,NULL);
     351       34999 :     pthread_mutex_init(&mt->pmut,NULL);
     352      460962 :     for (i=0;i<lim;i++)
     353             :     {
     354      425963 :       struct mt_queue *mq = mt->mq+i;
     355      425963 :       mq->no     = i;
     356      425963 :       mq->avma = 0;
     357      425963 :       mq->mainstack = NULL;
     358      425963 :       mq->worker = worker;
     359      425963 :       mq->input  = NULL;
     360      425963 :       mq->output = NULL;
     361      425963 :       mq->pcond  = &mt->pcond;
     362      425963 :       mq->pmut   = &mt->pmut;
     363      425963 :       pthread_cond_init(&mq->cond,NULL);
     364      425963 :       pthread_mutex_init(&mq->mut,NULL);
     365      425963 :       if (mtparisizemax)
     366           0 :         pari_thread_valloc(&mt->pth[i],mtparisize,mtparisizemax,(GEN)mq);
     367             :       else
     368      425963 :         pari_thread_alloc(&mt->pth[i],mtparisize,(GEN)mq);
     369             :     }
     370       34999 :     if (DEBUGLEVEL) pari_warn(warner,"starting %ld threads", lim);
     371       34999 :     BLOCK_SIGINT_START
     372      460962 :     for (i=0;i<lim;i++)
     373      425963 :       pthread_create(&mt->th[i],NULL, &mt_queue_run, (void*)&mt->pth[i]);
     374       34999 :     pari_mt = mt;
     375       34999 :     BLOCK_SIGINT_END
     376       34999 :     pt->get=&mtpthread_queue_get;
     377       34999 :     pt->submit=&mtpthread_queue_submit;
     378       34999 :     pt->end=&mt_queue_reset;
     379             :   }
     380       40514 : }

Generated by: LCOV version 1.13