--- /dev/null
+#include <minix/mthread.h>
+#include <string.h>
+#include "global.h"
+#include "proto.h"
+
+PRIVATE struct {
+ int used;
+ int nvalues;
+ void *mvalue;
+ void **value;
+ void (*destr)(void *);
+} keys[MTHREAD_KEYS_MAX];
+
+/*===========================================================================*
+ * mthread_init_keys *
+ *===========================================================================*/
+PUBLIC 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 *
+ *===========================================================================*/
+PUBLIC int mthread_key_create(mthread_key_t *key, void (*destructor)(void *))
+{
+/* Allocate a key.
+ */
+ mthread_key_t k;
+
+ mthread_init(); /* Make sure libmthread is initialized */
+
+ /* 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 *
+ *===========================================================================*/
+PUBLIC int mthread_key_delete(mthread_key_t key)
+{
+/* Free up a key, as well as any associated storage space.
+ */
+
+ mthread_init(); /* Make sure libmthread is initialized */
+
+ if (key < 0 || key >= MTHREAD_KEYS_MAX || !keys[key].used)
+ return(EINVAL);
+
+ free(keys[key].value);
+
+ keys[key].used = FALSE;
+
+ return(0);
+}
+
+/*===========================================================================*
+ * mthread_getspecific *
+ *===========================================================================*/
+PUBLIC void *mthread_getspecific(mthread_key_t key)
+{
+/* Get this thread's local value for the given key. The default is NULL.
+ */
+
+ mthread_init(); /* Make sure libmthread is initialized */
+
+ 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 *
+ *===========================================================================*/
+PUBLIC 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;
+
+ mthread_init(); /* Make sure libmthread is initialized */
+
+ 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 *
+ *===========================================================================*/
+PUBLIC 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;
+
+ /* 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);
+}
#define cond_t mthread_cond_t
#define once_t mthread_once_t
#define attr_t mthread_attr_t
+#define key_t mthread_key_t
#define MAX_ERROR 5
#include "common.c"
PRIVATE cond_t condition;
PRIVATE mutex_t *count_mutex, *condition_mutex;
PRIVATE once_t once;
+PRIVATE key_t key[MTHREAD_KEYS_MAX+1];
+PRIVATE int values[4];
+PRIVATE int first;
+
#define VERIFY_MUTEX(a,b,c,esub,eno) do { \
if (mutex_a_step != a) { \
printf("Expected %d %d %d, got: %d %d %d\n", \
FORWARD _PROTOTYPE( void test_mutex, (void) );
FORWARD _PROTOTYPE( void test_condition, (void) );
FORWARD _PROTOTYPE( void test_attributes, (void) );
+FORWARD _PROTOTYPE( void test_keys, (void) );
FORWARD _PROTOTYPE( void err, (int subtest, int error) );
/*===========================================================================*
#endif
}
+/*===========================================================================*
+ * destr_a *
+ *===========================================================================*/
+PRIVATE void destr_a(void *value)
+{
+ int num;
+
+ num = (int) value;
+
+ /* This destructor must be called once for all of the values 1..4. */
+ if (num <= 0 || num > 4) err(15, 1);
+
+ if (values[num - 1] != 1) err(15, 2);
+
+ values[num - 1] = 2;
+}
+
+/*===========================================================================*
+ * destr_b *
+ *===========================================================================*/
+PRIVATE void destr_b(void *value)
+{
+ /* This destructor must never trigger. */
+ err(16, 1);
+}
+
+/*===========================================================================*
+ * key_a *
+ *===========================================================================*/
+PRIVATE void key_a(void *arg)
+{
+ int i;
+
+ if (!first) mthread_yield();
+
+ /* Each new threads gets NULL-initialized values. */
+ for (i = 0; i < 5; i++)
+ if (mthread_getspecific(key[i]) != NULL) err(17, 1);
+
+ /* Make sure that the local values persist despite other threads' actions. */
+ for (i = 1; i < 5; i++)
+ if (mthread_setspecific(key[i], (void *) i) != 0) err(17, 2);
+
+ mthread_yield();
+
+ for (i = 1; i < 5; i++)
+ if (mthread_getspecific(key[i]) != (void *) i) err(17, 3);
+
+ mthread_yield();
+
+ /* The other thread has deleted this key by now. */
+ if (mthread_setspecific(key[3], NULL) != EINVAL) err(17, 4);
+
+ /* If a key's value is set to NULL, its destructor must not be called. */
+ if (mthread_setspecific(key[4], NULL) != 0) err(17, 5);
+}
+
+/*===========================================================================*
+ * key_b *
+ *===========================================================================*/
+PRIVATE void key_b(void *arg)
+{
+ int i;
+
+ first = 1;
+ mthread_yield();
+
+ /* Each new threads gets NULL-initialized values. */
+ for (i = 0; i < 5; i++)
+ if (mthread_getspecific(key[i]) != NULL) err(18, 1);
+
+ for (i = 0; i < 4; i++)
+ if (mthread_setspecific(key[i], (void *) (i + 2)) != 0) err(18, 2);
+
+ mthread_yield();
+
+ /* Deleting a key will not cause a call its destructor at any point. */
+ if (mthread_key_delete(key[3]) != 0) err(18, 3);
+
+ mthread_exit(NULL);
+}
+
+/*===========================================================================*
+ * key_c *
+ *===========================================================================*/
+PRIVATE void key_c(void *arg)
+{
+ /* The only thing that this thread should do, is set a value. */
+ if (mthread_setspecific(key[0], (void *) mthread_self()) != 0) err(19, 1);
+
+ mthread_yield();
+
+ if (!mthread_equal((thread_t) mthread_getspecific(key[0]), mthread_self()))
+ err(19, 2);
+}
+
+/*===========================================================================*
+ * test_keys *
+ *===========================================================================*/
+PRIVATE void test_keys(void)
+{
+ thread_t t[24];
+ int i, j;
+
+ /* Make sure that we can create exactly MTHREAD_KEYS_MAX keys. */
+ memset(key, 0, sizeof(key));
+
+ for (i = 0; i < MTHREAD_KEYS_MAX; i++) {
+ if (mthread_key_create(&key[i], NULL) != 0) err(20, 1);
+
+ for (j = 0; j < i - 1; j++)
+ if (key[i] == key[j]) err(20, 2);
+ }
+
+ if (mthread_key_create(&key[i], NULL) != EAGAIN) err(20, 3);
+
+ for (i = 3; i < MTHREAD_KEYS_MAX; i++)
+ if (mthread_key_delete(key[i]) != 0) err(20, 4);
+
+ /* Test basic good and bad value assignment and retrieval. */
+ if (mthread_setspecific(key[0], (void *) 1) != 0) err(20, 5);
+ if (mthread_setspecific(key[1], (void *) 2) != 0) err(20, 6);
+ if (mthread_setspecific(key[2], (void *) 3) != 0) err(20, 7);
+ if (mthread_setspecific(key[1], NULL) != 0) err(20, 8);
+ if (mthread_getspecific(key[0]) != (void *) 1) err(20, 9);
+ if (mthread_getspecific(key[1]) != NULL) err(20, 10);
+ if (mthread_getspecific(key[2]) != (void *) 3) err(20, 11);
+ if (mthread_setspecific(key[3], (void *) 4) != EINVAL) err(20, 12);
+ if (mthread_setspecific(key[3], NULL) != EINVAL) err(20, 13);
+
+ if (mthread_key_delete(key[1]) != 0) err(20, 14);
+ if (mthread_key_delete(key[2]) != 0) err(20, 15);
+
+ /* Test thread locality and destructors. */
+ if (mthread_key_create(&key[1], destr_a) != 0) err(20, 16);
+ if (mthread_key_create(&key[2], destr_a) != 0) err(20, 17);
+ if (mthread_key_create(&key[3], destr_b) != 0) err(20, 18);
+ if (mthread_key_create(&key[4], destr_b) != 0) err(20, 19);
+
+ if (mthread_getspecific(key[2]) != NULL) err(20, 20);
+
+ for (i = 0; i < 4; i++)
+ values[i] = 1;
+ first = 0;
+
+ if (mthread_create(&t[0], NULL, key_a, NULL) != 0) err(20, 21);
+ if (mthread_create(&t[1], NULL, key_b, NULL) != 0) err(20, 22);
+
+ for (i = 0; i < 2; i++)
+ if (mthread_join(t[i], NULL) != 0) err(20, 23);
+
+ /* The destructors must have changed all these values now. */
+ for (i = 0; i < 4; i++)
+ if (values[i] != 2) err(20, 24);
+
+ /* The original values must not have changed. */
+ if (mthread_getspecific(key[0]) != (void *) 1) err(20, 25);
+
+ /* Deleting a deleted key should not cause any problems either. */
+ if (mthread_key_delete(key[3]) != EINVAL) err(20, 26);
+
+ /* Make sure everything still works when using a larger number of threads.
+ * This should trigger reallocation code within libmthread's key handling.
+ */
+ for (i = 0; i < 24; i++)
+ if (mthread_create(&t[i], NULL, key_c, NULL) != 0) err(20, 27);
+
+ for (i = 0; i < 24; i++)
+ if (mthread_join(t[i], NULL) != 0) err(20, 28);
+}
+
/*===========================================================================*
* main *
*===========================================================================*/
test_mutex();
test_condition();
test_attributes();
+ test_keys();
quit();
}