minix3/lib/libmthread/key.c

191 lines
4.8 KiB
C

#include <minix/mthread.h>
#include <string.h>
#include "global.h"
#include "proto.h"
static int keys_used = 0;
static struct {
int used;
int nvalues;
void *mvalue;
void **value;
void (*destr)(void *);
} keys[MTHREAD_KEYS_MAX];
/*===========================================================================*
* mthread_init_keys *
*===========================================================================*/
void mthread_init_keys(void)
{
/* Initialize the table of key entries.
*/
mthread_key_t k;
for (k = 0; k < MTHREAD_KEYS_MAX; k++)
keys[k].used = FALSE;
}
/*===========================================================================*
* mthread_key_create *
*===========================================================================*/
int mthread_key_create(mthread_key_t *key, void (*destructor)(void *))
{
/* Allocate a key.
*/
mthread_key_t k;
keys_used = 1;
/* We do not yet allocate storage space for the values here, because we can
* not estimate how many threads will be created in the common case that the
* application creates keys before spawning threads.
*/
for (k = 0; k < MTHREAD_KEYS_MAX; k++) {
if (!keys[k].used) {
keys[k].used = TRUE;
keys[k].nvalues = 0;
keys[k].mvalue = NULL;
keys[k].value = NULL;
keys[k].destr = destructor;
*key = k;
return(0);
}
}
return(EAGAIN);
}
/*===========================================================================*
* mthread_key_delete *
*===========================================================================*/
int mthread_key_delete(mthread_key_t key)
{
/* Free up a key, as well as any associated storage space.
*/
if (key < 0 || key >= MTHREAD_KEYS_MAX || !keys[key].used)
return(EINVAL);
free(keys[key].value);
keys[key].used = FALSE;
return(0);
}
/*===========================================================================*
* mthread_getspecific *
*===========================================================================*/
void *mthread_getspecific(mthread_key_t key)
{
/* Get this thread's local value for the given key. The default is NULL.
*/
if (key < 0 || key >= MTHREAD_KEYS_MAX || !keys[key].used)
return(NULL);
if (current_thread == MAIN_THREAD)
return keys[key].mvalue;
if (current_thread < keys[key].nvalues)
return(keys[key].value[current_thread]);
return(NULL);
}
/*===========================================================================*
* mthread_setspecific *
*===========================================================================*/
int mthread_setspecific(mthread_key_t key, void *value)
{
/* Set this thread's value for the given key. Allocate more resources as
* necessary.
*/
void **p;
if (key < 0 || key >= MTHREAD_KEYS_MAX || !keys[key].used)
return(EINVAL);
if (current_thread == MAIN_THREAD) {
keys[key].mvalue = value;
return(0);
}
if (current_thread >= keys[key].nvalues) {
if (current_thread >= no_threads)
mthread_panic("Library state corrupt");
if ((p = (void **) realloc(keys[key].value,
sizeof(void*) * no_threads)) == NULL)
return(ENOMEM);
memset(&p[keys[key].nvalues], 0,
sizeof(void*) * (no_threads - keys[key].nvalues));
keys[key].nvalues = no_threads;
keys[key].value = p;
}
keys[key].value[current_thread] = value;
return(0);
}
/*===========================================================================*
* mthread_cleanup_values *
*===========================================================================*/
void mthread_cleanup_values(void)
{
/* Clean up all the values associated with an exiting thread, calling keys'
* destruction procedures as appropriate.
*/
mthread_key_t k;
void *value;
int found;
if (!keys_used) return; /* Only clean up if we used any keys at all */
/* Any of the destructors may set a new value on any key, so we may have to
* loop over the table of keys multiple times. This implementation has no
* protection against infinite loops in this case.
*/
do {
found = FALSE;
for (k = 0; k < MTHREAD_KEYS_MAX; k++) {
if (!keys[k].used) continue;
if (keys[k].destr == NULL) continue;
if (current_thread == MAIN_THREAD) {
value = keys[k].mvalue;
keys[k].mvalue = NULL;
} else {
if (current_thread >= keys[k].nvalues) continue;
value = keys[k].value[current_thread];
keys[k].value[current_thread] = NULL;
}
if (value != NULL) {
/* Note: calling mthread_exit() from a destructor
* causes undefined behavior.
*/
keys[k].destr(value);
found = TRUE;
}
}
} while (found);
}
/* pthread compatibility layer. */
__weak_alias(pthread_key_create, mthread_key_create)
__weak_alias(pthread_key_delete, mthread_key_delete)
__weak_alias(pthread_getspecific, mthread_getspecific)
__weak_alias(pthread_setspecific, mthread_setspecific)