diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h index c69385be9c..a91a016aae 100644 --- a/include/nuttx/sched.h +++ b/include/nuttx/sched.h @@ -375,6 +375,21 @@ struct pthread_cleanup_s }; #endif +/* type pthread_keyset_t *********************************************************/ +/* Smallest addressable type that can hold the entire configured number of keys */ + +#if defined(CONFIG_NPTHREAD_KEYS) && CONFIG_NPTHREAD_KEYS > 0 +# if CONFIG_NPTHREAD_KEYS > 32 +# error Too many pthread keys +# elif CONFIG_NPTHREAD_KEYS > 16 + typedef uint32_t pthread_keyset_t; +# elif CONFIG_NPTHREAD_KEYS > 8 + typedef uint16_t pthread_keyset_t; +# else + typedef uint8_t pthread_keyset_t; +# endif +#endif + /* struct dspace_s ***************************************************************/ /* This structure describes a reference counted D-Space region. This must be a * separately allocated "break-away" structure that can be owned by a task and @@ -520,7 +535,7 @@ struct task_group_s FAR struct join_s *tg_jointail; /* Tail of a list of join data */ #endif #if CONFIG_NPTHREAD_KEYS > 0 - uint8_t tg_nkeys; /* Number pthread keys allocated */ + pthread_keyset_t tg_keyset; /* Set of pthread keys allocated */ #endif #ifndef CONFIG_DISABLE_SIGNALS diff --git a/sched/Kconfig b/sched/Kconfig index e1219a9492..9cbc60d243 100644 --- a/sched/Kconfig +++ b/sched/Kconfig @@ -616,8 +616,10 @@ config NPTHREAD_KEYS int "Maximum number of pthread keys" default 4 if !DISABLE_PTHREAD default 0 if DISABLE_PTHREAD + range 0 32 ---help--- - The number of items of thread-specific data that can be retained + The number of items of thread-specific data that can be retained. + The value zero disables support for pthread-specific data. if !DISABLE_PTHREAD diff --git a/sched/pthread/pthread_getspecific.c b/sched/pthread/pthread_getspecific.c index d6c6f671b2..8bf4c86c82 100644 --- a/sched/pthread/pthread_getspecific.c +++ b/sched/pthread/pthread_getspecific.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/pthread/pthread_getspecific.c * - * Copyright (C) 2007, 2009, 2013 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2009, 2013, 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -88,11 +88,11 @@ FAR void *pthread_getspecific(pthread_key_t key) FAR struct task_group_s *group = rtcb->group; FAR void *ret = NULL; - DEBUGASSERT(group); + DEBUGASSERT(group != NULL && (unsigned)key < CONFIG_NPTHREAD_KEYS); /* Check if the key is valid. */ - if (key < group->tg_nkeys) + if (key < CONFIG_NPTHREAD_KEYS && (group->tg_keyset & (1 << key)) != 0) { /* Return the stored value. */ diff --git a/sched/pthread/pthread_keycreate.c b/sched/pthread/pthread_keycreate.c index 29f6c424d5..b08872ae73 100644 --- a/sched/pthread/pthread_keycreate.c +++ b/sched/pthread/pthread_keycreate.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/pthread/pthread_keycreate.c * - * Copyright (C) 2007-2009, 2013 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2013, 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -44,6 +44,8 @@ #include #include +#include + #include "sched/sched.h" #include "pthread/pthread.h" @@ -66,23 +68,23 @@ * associated with all defined keys in the new thread. * * Input Parameters: - * key = A pointer to the key to create. - * destructor = An optional destructor() function that may be associated - * with each key that is invoked when a thread exits. However, this - * argument is ignored in the current implementation. + * key - A pointer to the key to create. + * destructor - An optional destructor() function that may be associated + * with each key that is invoked when a thread exits. + * However, this argument is ignored in the current + * implementation. * * Returned Value: * If successful, the pthread_key_create() function will store the newly * created key value at *key and return zero (OK). Otherwise, an error - * number will bereturned to indicate the error: + * number will be returned to indicate the error: * - * EAGAIN - The system lacked sufficient resources to create another - * thread-specific data key, or the system-imposed limit on the total - * number of keys pers process {PTHREAD_KEYS_MAX} has been exceeded + * EAGAIN - The system lacked sufficient resources to create another + * thread-specific data key, or the system-imposed limit on + * the total number of keys pers process {PTHREAD_KEYS_MAX} + * has been exceeded * ENONMEM - Insufficient memory exists to create the key. * - * Assumptions: - * * POSIX Compatibility: * - The present implementation ignores the destructor argument. * @@ -94,25 +96,41 @@ int pthread_key_create(FAR pthread_key_t *key, #if CONFIG_NPTHREAD_KEYS > 0 FAR struct tcb_s *rtcb = this_task(); FAR struct task_group_s *group = rtcb->group; + irqstate_t flags; + int candidate; int ret = EAGAIN; - DEBUGASSERT(group); + DEBUGASSERT(key != NULL && group != NULL); - /* Check if we have exceeded the system-defined number of keys. */ + /* Search for an unused key. This is done in a critical section here to + * avoid concurrent modification of the group keyset. + */ - if (group->tg_nkeys < PTHREAD_KEYS_MAX) + flags = spin_lock_irqsave(); + for (candidate = 0; candidate < PTHREAD_KEYS_MAX; candidate++) { - /* Return the key value */ + /* Is this candidate key available? */ - *key = group->tg_nkeys; + pthread_keyset_t mask = (1 << candidate); + if ((group->tg_keyset & mask) == 0) + { + /* Yes.. allocate the key and break out of the loop */ - /* Increment the count of global keys. */ + group->tg_keyset |= mask; + break; + } + } - group->tg_nkeys++; + spin_unlock_irqrestore(flags); - /* Return success. */ + /* Check if found a valid key. */ - ret = OK; + if (candidate < PTHREAD_KEYS_MAX) + { + /* Yes.. Return the key value and success */ + + *key = candidate; + ret = OK; } return ret; diff --git a/sched/pthread/pthread_keydelete.c b/sched/pthread/pthread_keydelete.c index 2b3d8c9966..7aa9f30827 100644 --- a/sched/pthread/pthread_keydelete.c +++ b/sched/pthread/pthread_keydelete.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/pthread/pthread_keydelete.c * - * Copyright (C) 2007, 2009 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2009, 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -43,6 +43,9 @@ #include #include +#include + +#include "sched/sched.h" #include "pthread/pthread.h" /**************************************************************************** @@ -58,7 +61,7 @@ * does nothing in the present implementation. * * Input Parameters: - * key = the key to delete + * key - the key to delete * * Returned Value: * Always returns ENOSYS. @@ -71,6 +74,33 @@ int pthread_key_delete(pthread_key_t key) { +#if CONFIG_NPTHREAD_KEYS > 0 + FAR struct tcb_s *rtcb = this_task(); + FAR struct task_group_s *group = rtcb->group; + pthread_keyset_t mask; + irqstate_t flags; + int ret = EINVAL; + + DEBUGASSERT((unsigned)key < PTHREAD_KEYS_MAX && group != NULL); + if ((unsigned)key < PTHREAD_KEYS_MAX) + { + /* This is done in a critical section here to avoid concurrent + * modification of the group keyset. + */ + + mask = (1 << key); + flags = spin_lock_irqsave(); + + DEBUGASSERT((group->tg_keyset & mask) != 0); + group->tg_keyset &= ~mask; + spin_unlock_irqrestore(flags); + + ret = OK; + } + + return ret; +#else return ENOSYS; +#endif } diff --git a/sched/pthread/pthread_setspecific.c b/sched/pthread/pthread_setspecific.c index 166d3f23e3..9e46b01e61 100644 --- a/sched/pthread/pthread_setspecific.c +++ b/sched/pthread/pthread_setspecific.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/pthread/pthread_setspecific.c * - * Copyright (C) 2007-2009, 2013 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2013, 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -99,11 +99,11 @@ int pthread_setspecific(pthread_key_t key, FAR const void *value) FAR struct task_group_s *group = rtcb->group; int ret = EINVAL; - DEBUGASSERT(group); + DEBUGASSERT(group != NULL && (unsigned)key < CONFIG_NPTHREAD_KEYS); /* Check if the key is valid. */ - if (key < group->tg_nkeys) + if (key < CONFIG_NPTHREAD_KEYS && (group->tg_keyset & (1 << key)) != 0) { /* Store the data in the TCB. */