Como se ha explicado en el anterior capítulo, el mecanismo de "callback" requiere que se implemente una función que llamamos "thread id resolver". Esta función asigna un identificador numérico a cada "thread" que está en ejecución.
Esta función ha de reutilizar identificadores comenzando desde 0, es decir, que si un "thread" muere, el próximo que se creé debe tener el identificador del antiguo "thread". Así si en un determinado momento hay 8 threads en ejecución todos sus identificadores estarán entre 0 y 7.
Respetar esta restricción es importante, ya que se asociará una pila de
ejecución a cada "thread", y estas pilas estarán almacenadas en una tabla
de como mucho max_threads
(ver parámetro de configuración)
entradas. Si asignamos un identificador distinto a cada "thread" que se
cree podemos agotar pronto las entradas de dicha tabla y/o consumir más
memoria de la necesaria.
La función que identifica un "thread" no recibe ningún parámetro y sólo devuelve un entero positivo a partir de 0.
El "thread id resolver" se configura, habitualmente, desde el runtime
con las funciones _pdi_ebe_getThreadIdResolver()
y
_pdi_ebe_setThreadIdResolver()
. La primera obtiene un puntero al
"thread id resolver" actual, mientras que la segunda permite asignar uno nuevo.
Mostramos un ejemplo de runtime que instala un "thread id resolver" para la librería pthread:
/* Runtime de ejemplo que instala un "thread id resolver" para pthreads */ #include<config.h> #include<backend.h> #include<log.h> #include<pdiconfig.h> #include<threadid.h> #include<pthread.h> #define FALSE 0 #define TRUE (1==1) /* Esta es la tabla que contendrá la relación de threads en ejecución */ pthread_t *thconv = NULL; /* Usaremos un mutex para hacer los accesos a la tabla de forma atómica */ pthread_mutex_t mutex_thconv = PTHREAD_MUTEX_INITIALIZER; /* Esta función será la encarga de darle un identificador al thread */ static int idres(void) { int i; int fz; pthread_t pt; if(thconv) { /* Buscamos el thread */ pthread_mutex_lock(&mutex_thconv); pt = pthread_self(); fz = PDICFG.max_threads + 1; for(i = 0; i < PDICFG.max_threads; i++) { /* si lo encontramos lo devolvemos */ if(thconv[i] == pt) { pthread_mutex_unlock(&mutex_thconv); return i; } /* nos apuntamos esta posición, si está libre */ if(fz > PDICFG.max_threads && !thconv[i]) fz = i; } /* no hemos encontrado el thread, lo damos de alta */ thconv[fz] = pt; pthread_mutex_unlock(&mutex_thconv); return fz; } else return 0; } /* este wrapper se encargará de interceptar la muerte de un */ /* thread y así darlo de baja en la tabla 'thconv' */ int pthread_exit_wrapper(void *ret) { int id; if(thconv) { id = idres(); _pdi_log(THIS, "mato a %d\n", id); thconv[id] = 0; } pthread_exit(ret); } /* funciones de inicialización y finalización */ int PDI_BE_FUNC_INIT(void) { _pdi_log(THIS, "Inicializando backend '" __FILE__ "'.\n"); if(PDICFG.max_threads > 0) { if((thconv = malloc(sizeof(int) * PDICFG.max_threads)) == NULL) { _pdi_error(THIS, "No hubo memoria para 'thconv'."); return FALSE; } memset(thconv, 0, sizeof(int) * PDICFG.max_threads); _pdi_ebe_setThreadIdResolver(idres); } else { _pdi_log(THIS, "En este programa no se usan threads."); } return TRUE; } void PDI_BE_FUNC_FINI(void) { _pdi_log(THIS, "Cerrando mi backend.\n"); if(thconv) { free(thconv); thconv = NULL; _pdi_ebe_setThreadIdResolver(NULL); } }
Como podemos ver este runtime, sólo inicializarse instala, si
hay instrumentación con "threads", un "thread id resolver" e inicializa una
tabla de conversión. Esta tabla de conversión será ampliada cada vez que
se ejecute idres
() sobre un nuevo "thread".
La tabla de conversión mantiene una biyección entre un identificador pthread y el identificador numérico que le asignamos. Se dan de alta los nuevos "threads" de tal modo que siempre se les asignará el identificador más bajo posible.
Para mantener sólo los "threads" vivos en la tabla se instala una
redefinición sobre la función pthread_exit
() que va
dando de baja los "threads" que terminan. El fichero de comandos del
runtime sería el siguiente:
; Fichero de comandos runtime #backend RUNTIME runtime.so #object PTHREADS /lib/libpthread.so #commands D PTHREADS pthread_exit RUNTIME pthread_exit_wrapper
Es posible que no se hayan contemplado casos de la librería de pthread en este ejemplo, pero tampoco pretende ser un runtime exhaustivo y real. Simplemente se ilustra como se implementaría un pequeño sistema de obtención de identificadores de "thread".