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 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.10.0 lcov report (development 20777-d2a9243) Lines: 171 178 96.1 %
Date: 2017-06-25 05:59:24 Functions: 14 16 87.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             :   GEN input, output;
      26             :   GEN worker;
      27             :   long workid;
      28             :   pthread_cond_t cond;
      29             :   pthread_mutex_t mut;
      30             :   pthread_cond_t *pcond;
      31             :   pthread_mutex_t *pmut;
      32             : };
      33             : 
      34             : struct mt_pstate
      35             : {
      36             :   pthread_t *th;
      37             :   struct pari_thread *pth;
      38             :   struct mt_queue *mq;
      39             :   long n, nbint, last;
      40             :   long pending;
      41             :   pthread_cond_t pcond;
      42             :   pthread_mutex_t pmut;
      43             : };
      44             : 
      45             : static THREAD long mt_thread_no = -1;
      46             : static struct mt_pstate *pari_mt;
      47             : 
      48             : #define LOCK(x) pthread_mutex_lock(x); do
      49             : #define UNLOCK(x) while(0); pthread_mutex_unlock(x)
      50             : 
      51             : void
      52    96064094 : mt_sigint_block(void)
      53             : {
      54    96064094 :   if (mt_thread_no>=0)
      55     4976945 :     pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
      56    96799224 : }
      57             : 
      58             : void
      59    96393775 : mt_sigint_unblock(void)
      60             : {
      61    96393775 :   if (mt_thread_no>=0)
      62     5287465 :     pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
      63    96904207 : }
      64             : 
      65             : void
      66        1106 : mt_err_recover(long er)
      67             : {
      68             :   (void) er;
      69        1106 :   if (mt_thread_no>=0)
      70             :   {
      71           2 :     struct mt_pstate *mt = pari_mt;
      72           2 :     struct mt_queue *mq = mt->mq+mt_thread_no;
      73           2 :     GEN err = bin_copy(copy_bin(pari_err_last()));
      74           2 :     LOCK(mq->pmut)
      75             :     {
      76           2 :       mq->output = err;
      77           2 :       pthread_cond_signal(mq->pcond);
      78           2 :     } UNLOCK(mq->pmut);
      79           2 :     pthread_exit((void*)1);
      80             :   }
      81        1104 : }
      82             : 
      83             : void
      84           0 : mt_sigint(void)
      85             : {
      86           0 :   if (pari_mt) pthread_cond_broadcast(&pari_mt->pcond);
      87           0 : }
      88             : 
      89             : int
      90      191298 : mt_is_parallel(void)
      91             : {
      92      191298 :   return !!pari_mt;
      93             : }
      94             : 
      95             : int
      96    14218965 : mt_is_thread(void)
      97             : {
      98    14218965 :   return mt_thread_no>=0;
      99             : }
     100             : 
     101           0 : void mt_broadcast(GEN code) {(void) code;}
     102             : 
     103         209 : void pari_mt_init(void)
     104             : {
     105         209 :   pari_mt = NULL;
     106             : #ifdef _SC_NPROCESSORS_CONF
     107         209 :   if (!pari_mt_nbthreads) pari_mt_nbthreads = sysconf(_SC_NPROCESSORS_CONF);
     108             : #elif defined(_WIN32)
     109             :   if (!pari_mt_nbthreads) pari_mt_nbthreads = win32_nbthreads();
     110             : #else
     111             :   pari_mt_nbthreads = 1;
     112             : #endif
     113         209 : }
     114             : 
     115         209 : void pari_mt_close(void) { }
     116             : 
     117             : static void
     118       30685 : mt_queue_cleanup(void *arg)
     119             : {
     120             :   (void) arg;
     121       30685 :   pari_thread_close();
     122       30419 : }
     123             : 
     124             : static void*
     125       29902 : mt_queue_run(void *arg)
     126             : {
     127       29902 :   GEN args = pari_thread_start((struct pari_thread*) arg);
     128       30693 :   pari_sp av = avma;
     129       30693 :   struct mt_queue *mq = (struct mt_queue *) args;
     130       30693 :   mt_thread_no = mq->no;
     131       30693 :   pthread_cleanup_push(mt_queue_cleanup,NULL);
     132       30732 :   LOCK(mq->pmut)
     133             :   {
     134       30753 :     mq->avma = av;
     135       30753 :     pthread_cond_signal(mq->pcond);
     136       30753 :   } UNLOCK(mq->pmut);
     137             :   for(;;)
     138             :   {
     139             :     GEN work, done;
     140       99068 :     LOCK(&mq->mut)
     141             :     {
     142      264155 :       while(!mq->input)
     143       97593 :         pthread_cond_wait(&mq->cond, &mq->mut);
     144       67549 :     } UNLOCK(&mq->mut);
     145       68167 :     avma = mq->avma;
     146       68167 :     work = mq->input;
     147       68167 :     pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
     148       67709 :     done = closure_callgenvec(mq->worker,work);
     149       67481 :     pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
     150       68042 :     LOCK(mq->pmut)
     151             :     {
     152       68338 :       mq->avma = av;
     153       68338 :       mq->input = NULL;
     154       68338 :       mq->output = done;
     155       68338 :       pthread_cond_signal(mq->pcond);
     156       68338 :     } UNLOCK(mq->pmut);
     157       68317 :   }
     158             :   pthread_cleanup_pop(1);
     159             : #ifdef __GNUC__
     160             :   return NULL; /* LCOV_EXCL_LINE */
     161             : #endif
     162             : }
     163             : 
     164             : static long
     165       99172 : mt_queue_check(struct mt_pstate *mt)
     166             : {
     167             :   long i;
     168      829992 :   for(i=0; i<mt->n; i++)
     169             :   {
     170      799158 :     struct mt_queue *mq = mt->mq+i;
     171      799158 :     if (mq->output) return i;
     172             :   }
     173       30834 :   return -1;
     174             : }
     175             : 
     176             : static GEN
     177       94563 : mtpthread_queue_get(struct mt_state *junk, long *workid, long *pending)
     178             : {
     179       94563 :   struct mt_pstate *mt = pari_mt;
     180             :   struct mt_queue *mq;
     181       94563 :   GEN done = NULL;
     182             :   long last;
     183             :   (void) junk;
     184       94563 :   if (mt->nbint<mt->n)
     185             :   {
     186       26223 :     mt->last = mt->nbint;
     187       26223 :     *pending = mt->pending;
     188       26223 :     return NULL;
     189             :   }
     190       68340 :   BLOCK_SIGINT_START
     191       68340 :   LOCK(&mt->pmut)
     192             :   {
     193      167512 :     while ((last = mt_queue_check(mt)) < 0)
     194             :     {
     195       30834 :       pthread_cond_wait(&mt->pcond, &mt->pmut);
     196       30834 :       if (PARI_SIGINT_pending)
     197             :       {
     198           2 :         int sig = PARI_SIGINT_pending;
     199           2 :         PARI_SIGINT_pending = 0;
     200           2 :         pthread_mutex_unlock(&mt->pmut);
     201           2 :         PARI_SIGINT_block = 0;
     202           2 :         raise(sig);
     203           0 :         PARI_SIGINT_block = 1;
     204           0 :         pthread_mutex_lock(&mt->pmut);
     205             :       }
     206             :     }
     207       68338 :   } UNLOCK(&mt->pmut);
     208       68338 :   BLOCK_SIGINT_END
     209       68338 :   mq = mt->mq+last;
     210       68338 :   if (mq->output==err_e_STACK) pari_err(e_STACKTHREAD);
     211       68338 :   done = gcopy(mq->output);
     212       68338 :   mq->output = NULL;
     213       68338 :   if (workid) *workid = mq->workid;
     214       68338 :   if (typ(done)==t_ERROR) pari_err(0,done);
     215       68336 :   mt->last = last;
     216       68336 :   mt->pending--;
     217       68336 :   *pending = mt->pending;
     218       68336 :   return done;
     219             : }
     220             : 
     221             : static void
     222       94563 : mtpthread_queue_submit(struct mt_state *junk, long workid, GEN work)
     223             : {
     224       94563 :   struct mt_pstate *mt = pari_mt;
     225       94563 :   struct mt_queue *mq = mt->mq+mt->last;
     226             :   (void) junk;
     227      189126 :   if (!work) { mt->nbint=mt->n; return; }
     228       68388 :   BLOCK_SIGINT_START
     229       68388 :   if (mt->nbint<mt->n)
     230             :   {
     231       30471 :     mt->nbint++;
     232       30471 :     LOCK(mq->pmut)
     233             :     {
     234       84408 :       while(!mq->avma)
     235       23466 :         pthread_cond_wait(mq->pcond, mq->pmut);
     236       30471 :     } UNLOCK(mq->pmut);
     237             :   }
     238       68388 :   LOCK(&mq->mut)
     239             :   {
     240       68388 :     mq->output = NULL;
     241       68388 :     mq->workid = workid;
     242       68388 :     mq->input = gcopy_avma(work, &mq->avma);
     243       68388 :     pthread_cond_signal(&mq->cond);
     244       68388 :   } UNLOCK(&mq->mut);
     245       68388 :   mt->pending++;
     246       68388 :   BLOCK_SIGINT_END
     247             : }
     248             : 
     249             : void
     250        4294 : mt_queue_reset(void)
     251             : {
     252        4294 :   struct mt_pstate *mt = pari_mt;
     253             :   long i;
     254        4294 :   BLOCK_SIGINT_START
     255       35047 :   for (i=0; i<mt->n; i++)
     256       30753 :     pthread_cancel(mt->th[i]);
     257       35047 :   for (i=0; i<mt->n; i++)
     258       30753 :     pthread_join(mt->th[i],NULL);
     259        4294 :   pari_mt = NULL;
     260        4294 :   BLOCK_SIGINT_END
     261        4294 :   if (DEBUGLEVEL) pari_warn(warner,"stop threads");
     262       35047 :   for (i=0;i<mt->n;i++)
     263             :   {
     264       30753 :     struct mt_queue *mq = mt->mq+i;
     265       30753 :     pthread_cond_destroy(&mq->cond);
     266       30753 :     pthread_mutex_destroy(&mq->mut);
     267       30753 :     pari_thread_free(&mt->pth[i]);
     268             :   }
     269        4294 :   pari_free(mt->mq);
     270        4294 :   pari_free(mt->pth);
     271        4294 :   pari_free(mt->th);
     272        4294 :   pari_free(mt);
     273        4294 : }
     274             : 
     275             : void
     276        4627 : mt_queue_start_lim(struct pari_mt *pt, GEN worker, long lim)
     277             : {
     278        4627 :   if (lim==0) lim = pari_mt_nbthreads;
     279        4561 :   else        lim = minss(pari_mt_nbthreads, lim);
     280        4627 :   if (pari_mt || lim <= 1)
     281         333 :     mtsingle_queue_start(pt, worker);
     282             :   else
     283             :   {
     284        4294 :     struct mt_pstate *mt =
     285             :            (struct mt_pstate*) pari_malloc(sizeof(struct mt_pstate));
     286        4294 :     long mtparisize = GP_DATA->threadsize? GP_DATA->threadsize: pari_mainstack->rsize;
     287        4294 :     long mtparisizemax = GP_DATA->threadsizemax;
     288             :     long i;
     289        4294 :     mt->mq  = (struct mt_queue *) pari_malloc(sizeof(*mt->mq)*lim);
     290        4294 :     mt->th  = (pthread_t *) pari_malloc(sizeof(*mt->th)*lim);
     291        4294 :     mt->pth = (struct pari_thread *) pari_malloc(sizeof(*mt->pth)*lim);
     292        4294 :     mt->pending = 0;
     293        4294 :     mt->n = lim;
     294        4294 :     mt->nbint = 0;
     295        4294 :     mt->last = 0;
     296        4294 :     pthread_cond_init(&mt->pcond,NULL);
     297        4294 :     pthread_mutex_init(&mt->pmut,NULL);
     298        4294 :     pari_thread_sync();
     299       35047 :     for (i=0;i<lim;i++)
     300             :     {
     301       30753 :       struct mt_queue *mq = mt->mq+i;
     302       30753 :       mq->no     = i;
     303       30753 :       mq->avma   = 0;
     304       30753 :       mq->worker = worker;
     305       30753 :       mq->input  = NULL;
     306       30753 :       mq->output = NULL;
     307       30753 :       mq->pcond  = &mt->pcond;
     308       30753 :       mq->pmut   = &mt->pmut;
     309       30753 :       pthread_cond_init(&mq->cond,NULL);
     310       30753 :       pthread_mutex_init(&mq->mut,NULL);
     311       30753 :       if (mtparisizemax)
     312           0 :         pari_thread_valloc(&mt->pth[i],mtparisize,mtparisizemax,(GEN)mq);
     313             :       else
     314       30753 :         pari_thread_alloc(&mt->pth[i],mtparisize,(GEN)mq);
     315             :     }
     316        4294 :     if (DEBUGLEVEL) pari_warn(warner,"start threads");
     317        4294 :     BLOCK_SIGINT_START
     318       35047 :     for (i=0;i<lim;i++)
     319       30753 :       pthread_create(&mt->th[i],NULL, &mt_queue_run, (void*)&mt->pth[i]);
     320        4294 :     pari_mt = mt;
     321        4294 :     BLOCK_SIGINT_END
     322        4294 :     pt->get=&mtpthread_queue_get;
     323        4294 :     pt->submit=&mtpthread_queue_submit;
     324        4294 :     pt->end=&mt_queue_reset;
     325             :   }
     326        4627 : }

Generated by: LCOV version 1.11