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