Provided by: liblcgdm-dev_1.13.0-1_amd64 bug

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