Man Linux: Main Page and Category List

NAME

       Cpool - LCG Pool inferface

SYNOPSIS

       #include <Cpool_api.h>

       int Cpool_create(int nbwanted, int * nbget);

       int  Cpool_assign(int poolid, void *(*startroutine)(void *), void *arg,
       int timeout);

       int Cpool_next_index(int poolid);

       int Cpool_next_index_timeout(int poolid, int timeout);

ERRORS

       See Cthread corresponding section.

DESCRIPTION

       (Please read the NOTE section)

       Cpool is a layer built upon  Cthread,  the  LCG  Thread  interface.  It
       allows the user to create dedicated pools, and then to assign to one of
       them a given routine to execute.

       The created processes or threads will remain alive, unless the routines
       assigned to are crashing, or explicitly calling an exit statement, like
       exit() or pthread_exit().

       Typical use might be writing a server,  with  a  bunch  of  pre-created
       processes or pools (depending on the environment with which Cthread has
       been compiled), and assign to a given pool a routine  with  the  socket
       file descriptor as argument address.

       In principle Cpool should be compiled with the same relevant flags with
       which Cthread has been.

       int Cpool_create(int nbwanted, int * nbget);

       This method is creating a pool of nbwanted processes or threads. If the
       second  argument,  nbget  ,  is not NULL, its location will contain the
       number of effectively created threads or processes.

       Return value is the pool ID, a number greater or equal to zero,  or  -1
       in case of error.

       int  Cpool_assign(int poolid, void *(*startroutine)(void *), void *arg,
       int timeout);

       This  method  is  assigning  a  routine  to  poolid  as   returned   by
       Cpool_create,  whose  address  is  startroutine  ,  that  have the same
       prototype as every typical routine  in  multithread  programming.  This
       means  that  it  returns  a  pointer,  and  it  gets as entry a pointer
       identified by the arg  parameter.  The  last  argument  is  a  possible
       timeout  ,  in seconds, which will apply if it is greater than zero. If
       it is lower than zero, the assignment will wait forever until a  thread
       is  available.  If  it  is  equal  to  zero,  the  method  will  return
       immediately if no thread is available.

       Return value is 0 if success, or -1 in case of error.

       int Cpool_next_index(int poolid);

       int Cpool_next_index_timeout(int poolid, int timeout);

       Those methods returns that next available thread number  that  will  be
       assigned  if  you  ever  call  Cpool_assign  immediately  after. If you
       specify a timeout lower or equal than zero, then  this  is  a  blocking
       method  until  one  thread  is  available  at least. Those methods, so,
       returns a number greater or equal than zero, and  -1  if  there  is  an
       error.

NOTE

              Arguments passing in a non-thread environment

              Since  a  forked  process  can  only  address its namespace data
              segment, the address of the arguments,  if  any,  valid  in  its
              parent,  will  not  be  directly accessible for the child we are
              talking about.

              This means that Cpool, in  a  non-thread  environment,  have  to
              trace-back  all  the  memory  allocation visible for the parent.
              Then, Cpool is not passing the address of the arguments, but its
              content  to  the  child  through  a  child-parent communication,
              monitored with a simple protocol.

              There are four cases:
                         1.The address is NULL: nothing will be transmitted to
                     the child
                         2.The  address  is  exactly  a  pointer  returned  by
                     malloc() or realloc(): the full  malloced  area  will  be
                     tranmitted.
                         3.The  address  is  somewhere  in  a  memory allocate
                     block: the remaining memory block, e.g. starting from the
                     address up to its end, will be transmitted.
                         4.the  address  do  not point to any memory allocated
                     area: Cpool will assume it is  a  pointer-like  argument,
                     probably  to  some  static  variables,  visible  for  all
                     processes, and will transmit the content  of  the  memory
                     pointed  by the address, assuming it is coded on 64-bits.
              In any case, the user is passing a pointer, and the routine will
              see  a  pointer,  pointing  to  a  (hopefully, see the point 4.,
              listed upper) same-content area.

              Arguments  design  to  work  on  both  thread   and   non-thread
              environments

              The  thread  and  non-thread  arguments  can have conceptually a
              different design when dealing with arguments;

              In a thread environment, the routined passed to Cpool_assign, is
              sharing  memory,  so  is allowed to free() the argument, because
              memory is  then  shared.   On  the  contrary,  in  a  non-thread
              environment, this may be a segmentation fault.

              This  means  that  it  is  recommended  to use static variables,
              containing simple value, like an integer (for example: a  socket
              file descriptor), and not allocated memory. If, neverthless, you
              persist to use free() in your routine, you can use the following
              trick:

              /* ------------------------ */
              /* In the Caller Routine    */
              /* ------------------------ */

              arg = malloc(...);

              if (! Cpool_assign(...)) {
                if (Cthread_environment() != CTHREAD_TRUE_THREAD) {
                  /* Non-Thread environment */
                  free(arg);
                } else {
                  /* Thread environment     */
                  /* ... do nothing         */
                }
              } else {
                  /* In cany case it is OK  */
                  free(arg);
              }

              /* ------------------------ */
              /* In the Execution Routine */
              /* ------------------------ */

              void *routine(void *arg) {
                ./..
                if (Cthread_environment() == CTHREAD_TRUE_THREAD) {
                  /* Thread environment */
                  free(arg);
                } else {
                  /* Non-Thread environment */
                  /* ... do nothing         */
                }
                ./..
              }

EXAMPLE

       #include <Cpool_api.h>
       #include <stdio.h>
       #include <errno.h>

       #define NPOOL 2
       #define PROCS_PER_POOL 2
       #define TIMEOUT 2
       void *testit(void *);

       int main() {
         int pid;
         int i, j;
         int ipool[NPOOL];
         int npool[NPOOL];
         int *arg;

         pid = getpid();

         printf("... Defining %d pools with %d elements each\n",
                NPOOL,PROCS_PER_POOL);

         for (i=0; i < NPOOL; i++) {
           if ((ipool[i] = Cpool_create(PROCS_PER_POOL,&(npool[i]))) < 0) {
             printf("### Error No %d creating pool (%s)\n",
                    errno,strerror(errno));
           } else {
             printf("... Pool No %d created with %d processes\n",
                    ipool[i],npool[i]);
           }
         }

         for (i=0; i < NPOOL; i++) {
           /* Loop on the number of processes + 1 ... */
           for (j=0; j <= npool[i]; j++) {
             if ((arg = malloc(sizeof(int))) == NULL) {
               printf("### Malloc error, errno = %d (%s)\n",
                      errno,strerror(errno));
               continue;
             }
             *arg = i*10+j;
             printf("... Assign to pool %d (timeout=%d) the %d-th routine 0x%x(%d)\n",
                    ipool[i],TIMEOUT,j+1,(unsigned int) testit,*arg);
             if (Cpool_assign(ipool[i], testit, arg, TIMEOUT)) {
               printf("### Can’t assign to pool No %d (errno=%d [%s]) the %d-th routine\n",
                      ipool[i],errno,strerror(errno),j);
               free(arg);
             } else {
               printf("... Okay for assign to pool No %d of the %d-th routine\n",
                      ipool[i],j);
               If (Cthread_environment() != CTHREAD_TRUE_THREAD) {
                 /* Non-thread environment: the child is in principle not allowed */
                 /* to do free himself                                            */
                 free(arg);
               }
             }
           }
         }

         /* We wait enough time for our threads to terminate... */
         sleep(TIMEOUT*NPOOL*PROCS_PER_POOL);

         exit(EXIT_SUCCESS);
       }

       void *testit(void *arg) {
         int caller_pid, my_pid;

         my_pid = getpid();

         caller_pid = (int) * (int *) arg;

         if (Cthread_environment() == CTHREAD_TRUE_THREAD) {
           /* Thread environment : we free the memory */
           free(arg);
         }

         printf("... I am PID=%d called by pool %d, try No %d\n",
                my_pid,caller_pid/10,caller_pid - 10*(caller_pid/10));

         /*
          * Wait up to the timeout + 1
          */
         sleep(TIMEOUT*2);

         return(NULL);
       }

SEE ALSO

       Cthread

AUTHOR

       LCG Grid Deployment Team