libmthread: support for thread-local storage (keys/specifics)
This commit is contained in:
parent
dc8ee363db
commit
020277a38f
7 changed files with 384 additions and 3 deletions
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
typedef int mthread_thread_t;
|
typedef int mthread_thread_t;
|
||||||
typedef int mthread_once_t;
|
typedef int mthread_once_t;
|
||||||
|
typedef int mthread_key_t;
|
||||||
typedef void * mthread_condattr_t;
|
typedef void * mthread_condattr_t;
|
||||||
typedef void * mthread_mutexattr_t;
|
typedef void * mthread_mutexattr_t;
|
||||||
|
|
||||||
|
@ -58,6 +59,7 @@ typedef struct __mthread_attr *mthread_attr_t;
|
||||||
#define MTHREAD_CREATE_DETACHED 002
|
#define MTHREAD_CREATE_DETACHED 002
|
||||||
#define MTHREAD_ONCE_INIT 0
|
#define MTHREAD_ONCE_INIT 0
|
||||||
#define MTHREAD_STACK_MIN MINSIGSTKSZ
|
#define MTHREAD_STACK_MIN MINSIGSTKSZ
|
||||||
|
#define MTHREAD_KEYS_MAX 128
|
||||||
|
|
||||||
/* allocate.c */
|
/* allocate.c */
|
||||||
_PROTOTYPE( int mthread_create, (mthread_thread_t *thread,
|
_PROTOTYPE( int mthread_create, (mthread_thread_t *thread,
|
||||||
|
@ -99,6 +101,13 @@ _PROTOTYPE( int mthread_cond_signal, (mthread_cond_t *cond) );
|
||||||
_PROTOTYPE( int mthread_cond_wait, (mthread_cond_t *cond,
|
_PROTOTYPE( int mthread_cond_wait, (mthread_cond_t *cond,
|
||||||
mthread_mutex_t *mutex) );
|
mthread_mutex_t *mutex) );
|
||||||
|
|
||||||
|
/* key.c */
|
||||||
|
_PROTOTYPE( int mthread_key_create, (mthread_key_t *key,
|
||||||
|
void (*destructor)(void *)) );
|
||||||
|
_PROTOTYPE( int mthread_key_delete, (mthread_key_t key) );
|
||||||
|
_PROTOTYPE( void *mthread_getspecific, (mthread_key_t key) );
|
||||||
|
_PROTOTYPE( int mthread_setspecific, (mthread_key_t key, void *value) );
|
||||||
|
|
||||||
/* misc.c */
|
/* misc.c */
|
||||||
_PROTOTYPE( void mthread_stats, (void) );
|
_PROTOTYPE( void mthread_stats, (void) );
|
||||||
_PROTOTYPE( void mthread_verify_f, (char *f, int l) );
|
_PROTOTYPE( void mthread_verify_f, (char *f, int l) );
|
||||||
|
|
|
@ -11,6 +11,7 @@ SRCS= \
|
||||||
misc.c \
|
misc.c \
|
||||||
queue.c \
|
queue.c \
|
||||||
condition.c \
|
condition.c \
|
||||||
scheduler.c
|
scheduler.c \
|
||||||
|
key.c
|
||||||
|
|
||||||
.include <bsd.lib.mk>
|
.include <bsd.lib.mk>
|
||||||
|
|
|
@ -121,6 +121,8 @@ void *value;
|
||||||
if (tcb->m_state == MS_EXITING) /* Already stopping, nothing to do. */
|
if (tcb->m_state == MS_EXITING) /* Already stopping, nothing to do. */
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
mthread_cleanup_values();
|
||||||
|
|
||||||
/* When we're called from the fallback thread, the fallback thread
|
/* When we're called from the fallback thread, the fallback thread
|
||||||
* will invoke the scheduler. However, if the thread itself called
|
* will invoke the scheduler. However, if the thread itself called
|
||||||
* mthread_exit, _we_ will have to wake up the scheduler.
|
* mthread_exit, _we_ will have to wake up the scheduler.
|
||||||
|
@ -283,6 +285,7 @@ PUBLIC void mthread_init(void)
|
||||||
mthread_init_valid_mutexes();
|
mthread_init_valid_mutexes();
|
||||||
mthread_init_valid_conditions();
|
mthread_init_valid_conditions();
|
||||||
mthread_init_valid_attributes();
|
mthread_init_valid_attributes();
|
||||||
|
mthread_init_keys();
|
||||||
mthread_init_scheduler();
|
mthread_init_scheduler();
|
||||||
|
|
||||||
/* Initialize the fallback thread */
|
/* Initialize the fallback thread */
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
#define NO_THREADS 4
|
#define NO_THREADS 4
|
||||||
#define MAX_THREAD_POOL 1024
|
#define MAX_THREAD_POOL 1024
|
||||||
#define STACKSZ 4096
|
#define STACKSZ 4096
|
||||||
#define MAIN_THREAD -1
|
#define MAIN_THREAD (-1)
|
||||||
#define NO_THREAD -2
|
#define NO_THREAD (-2)
|
||||||
#define isokthreadid(i) (i == MAIN_THREAD || (i >= 0 && i < no_threads))
|
#define isokthreadid(i) (i == MAIN_THREAD || (i >= 0 && i < no_threads))
|
||||||
#define MTHREAD_INIT_MAGIC 0xca11ab1e
|
#define MTHREAD_INIT_MAGIC 0xca11ab1e
|
||||||
#define MTHREAD_NOT_INUSE 0xdefec7
|
#define MTHREAD_NOT_INUSE 0xdefec7
|
||||||
|
|
186
lib/libmthread/key.c
Normal file
186
lib/libmthread/key.c
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
#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);
|
||||||
|
}
|
|
@ -16,6 +16,10 @@ _PROTOTYPE( void mthread_init_valid_conditions, (void) );
|
||||||
_PROTOTYPE( int mthread_cond_verify, (void) );
|
_PROTOTYPE( int mthread_cond_verify, (void) );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* key.c */
|
||||||
|
_PROTOTYPE( void mthread_init_keys, (void) );
|
||||||
|
_PROTOTYPE( void mthread_cleanup_values, (void) );
|
||||||
|
|
||||||
/* misc.c */
|
/* misc.c */
|
||||||
#define mthread_panic(m) mthread_panic_f(__FILE__, __LINE__, (m))
|
#define mthread_panic(m) mthread_panic_f(__FILE__, __LINE__, (m))
|
||||||
_PROTOTYPE( void mthread_panic_f, (const char *file, int line,
|
_PROTOTYPE( void mthread_panic_f, (const char *file, int line,
|
||||||
|
|
178
test/test59.c
178
test/test59.c
|
@ -9,6 +9,7 @@
|
||||||
#define cond_t mthread_cond_t
|
#define cond_t mthread_cond_t
|
||||||
#define once_t mthread_once_t
|
#define once_t mthread_once_t
|
||||||
#define attr_t mthread_attr_t
|
#define attr_t mthread_attr_t
|
||||||
|
#define key_t mthread_key_t
|
||||||
|
|
||||||
#define MAX_ERROR 5
|
#define MAX_ERROR 5
|
||||||
#include "common.c"
|
#include "common.c"
|
||||||
|
@ -21,6 +22,10 @@ PRIVATE mutex_t mu[3];
|
||||||
PRIVATE cond_t condition;
|
PRIVATE cond_t condition;
|
||||||
PRIVATE mutex_t *count_mutex, *condition_mutex;
|
PRIVATE mutex_t *count_mutex, *condition_mutex;
|
||||||
PRIVATE once_t once;
|
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 { \
|
#define VERIFY_MUTEX(a,b,c,esub,eno) do { \
|
||||||
if (mutex_a_step != a) { \
|
if (mutex_a_step != a) { \
|
||||||
printf("Expected %d %d %d, got: %d %d %d\n", \
|
printf("Expected %d %d %d, got: %d %d %d\n", \
|
||||||
|
@ -47,6 +52,7 @@ FORWARD _PROTOTYPE( void test_scheduling, (void) );
|
||||||
FORWARD _PROTOTYPE( void test_mutex, (void) );
|
FORWARD _PROTOTYPE( void test_mutex, (void) );
|
||||||
FORWARD _PROTOTYPE( void test_condition, (void) );
|
FORWARD _PROTOTYPE( void test_condition, (void) );
|
||||||
FORWARD _PROTOTYPE( void test_attributes, (void) );
|
FORWARD _PROTOTYPE( void test_attributes, (void) );
|
||||||
|
FORWARD _PROTOTYPE( void test_keys, (void) );
|
||||||
FORWARD _PROTOTYPE( void err, (int subtest, int error) );
|
FORWARD _PROTOTYPE( void err, (int subtest, int error) );
|
||||||
|
|
||||||
/*===========================================================================*
|
/*===========================================================================*
|
||||||
|
@ -724,6 +730,177 @@ PRIVATE void test_attributes(void)
|
||||||
#endif
|
#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 *
|
* main *
|
||||||
*===========================================================================*/
|
*===========================================================================*/
|
||||||
|
@ -740,6 +917,7 @@ int main(void)
|
||||||
test_mutex();
|
test_mutex();
|
||||||
test_condition();
|
test_condition();
|
||||||
test_attributes();
|
test_attributes();
|
||||||
|
test_keys();
|
||||||
quit();
|
quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue