bionic (3) Cpool.3.gz

Provided by: liblcgdm-dev_1.10.0-2_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