diff --git a/include/nuttx/spinlock.h b/include/nuttx/spinlock.h index dc551e6d2b..2e5beb3745 100644 --- a/include/nuttx/spinlock.h +++ b/include/nuttx/spinlock.h @@ -384,9 +384,11 @@ void spin_unlockr(FAR struct spinlock_s *lock); * ****************************************************************************/ +#ifdef CONFIG_SMP void spin_setbit(FAR volatile cpu_set_t *set, unsigned int cpu, FAR volatile spinlock_t *setlock, FAR volatile spinlock_t *orlock); +#endif /**************************************************************************** * Name: spin_clrbit @@ -405,9 +407,11 @@ void spin_setbit(FAR volatile cpu_set_t *set, unsigned int cpu, * ****************************************************************************/ +#ifdef CONFIG_SMP void spin_clrbit(FAR volatile cpu_set_t *set, unsigned int cpu, FAR volatile spinlock_t *setlock, FAR volatile spinlock_t *orlock); +#endif #endif /* CONFIG_SPINLOCK */ #endif /* __INCLUDE_NUTTX_SPINLOCK_H */ diff --git a/include/pthread.h b/include/pthread.h index 188de4967c..b1ac5ab249 100644 --- a/include/pthread.h +++ b/include/pthread.h @@ -55,6 +55,20 @@ #include /* For sem_t and SEM_PRIO_* defines */ +#ifdef CONFIG_PTHREAD_SPINLOCKS +/* The architecture specific spinlock.h header file must provide the + * following: + * + * SP_LOCKED - A definition of the locked state value (usually 1) + * SP_UNLOCKED - A definition of the unlocked state value (usually 0) + * spinlock_t - The type of a spinlock memory object. + * + * SP_LOCKED and SP_UNLOCKED must constants of type spinlock_t. + */ + +# include +#endif + /******************************************************************************** * Pre-processor Definitions ********************************************************************************/ @@ -381,6 +395,25 @@ typedef int pthread_rwlockattr_t; PTHREAD_COND_INITIALIZER, \ 0, 0, false} +#ifdef CONFIG_PTHREAD_SPINLOCKS +#ifndef __PTHREAD_SPINLOCK_T_DEFINED +/* This (non-standard) structure represents a pthread spinlock */ + +struct pthread_spinlock_s +{ + volatile spinlock_t sp_lock; /* Indicates if the spinlock is locked or + * not. See the* values SP_LOCKED and + * SP_UNLOCKED. */ + pthread_t sp_holder; /* ID of the thread that holds the spinlock */ +}; + +/* It is referenced via this standard type */ + +typedef FAR struct pthread_spinlock_s pthread_spinlock_t; +#define __PTHREAD_SPINLOCK_T_DEFINED 1 +#endif +#endif /* JCONFIG_PTHREAD_SPINLOCKS */ + #ifdef CONFIG_PTHREAD_CLEANUP /* This type describes the pthread cleanup callback (non-standard) */ @@ -622,6 +655,16 @@ int pthread_rwlock_unlock(FAR pthread_rwlock_t *lock); int pthread_kill(pthread_t thread, int sig); int pthread_sigmask(int how, FAR const sigset_t *set, FAR sigset_t *oset); +#ifdef CONFIG_PTHREAD_SPINLOCKS +/* Pthread spinlocks */ + +int pthread_spin_init(FAR pthread_spinlock_t *lock, int pshared); +int pthread_spin_destroy(FAR pthread_spinlock_t *lock); +int pthread_spin_lock(FAR pthread_spinlock_t *lock); +int pthread_spin_trylock(FAR pthread_spinlock_t *lock); +int pthread_spin_unlock(FAR pthread_spinlock_t *lock); +#endif + #ifdef __cplusplus } #endif @@ -695,6 +738,14 @@ typedef struct pthread_barrier_s pthread_barrier_t; # define __PTHREAD_BARRIER_T_DEFINED 1 #endif +#ifdef CONFIG_PTHREAD_SPINLOCKS +#ifndef __PTHREAD_SPINLOCK_T_DEFINED +struct pthread_spinlock_s; +typedef FAR struct pthread_spinlock_s pthread_spinlock_t; +#define __PTHREAD_SPINLOCK_T_DEFINED 1 +#endif +#endif /* CONFIG_PTHREAD_SPINLOCKS */ + #ifndef __PTHREAD_ONCE_T_DEFINED typedef bool pthread_once_t; # define __PTHREAD_ONCE_T_DEFINED 1 diff --git a/libs/libc/Kconfig b/libs/libc/Kconfig index 1f7943f7b9..78c64ad419 100644 --- a/libs/libc/Kconfig +++ b/libs/libc/Kconfig @@ -11,6 +11,7 @@ source libs/libc/machine/Kconfig source libs/libc/stdlib/Kconfig source libs/libc/unistd/Kconfig source libs/libc/string/Kconfig +source libs/libc/pthread/Kconfig source libs/libc/dlfcn/Kconfig source libs/libc/modlib/Kconfig source libs/libc/wchar/Kconfig diff --git a/libs/libc/pthread/Kconfig b/libs/libc/pthread/Kconfig new file mode 100644 index 0000000000..e20b25ebed --- /dev/null +++ b/libs/libc/pthread/Kconfig @@ -0,0 +1,26 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menu "pthread support" + depends on !CONFIG_DISABLE_PTHREAD + +config PTHREAD_SPINLOCKS + bool "pthread spinlock support" + default n + depends on SPINLOCK && BUILD_FLAT && EXPERIMENTAL + ---help--- + Enable EXPERIMENTAL support for pthread spinlocks. + + This is marked EXPERIMENTAL for two reasons (1) the use case is not + fully verified, and (2) it depends on architecture specific + support provided by each architecture that may not be fully + available to the pthread library. + + It also currently depends on CONFIG_BUILD_FLAT because the + critical test and set function (up_testset()) as prototyped in + include/nuttx/spinlock() does not permit an inline function or + macro to be used in user-mode application space. + +endmenu # pthread support diff --git a/libs/libc/pthread/Make.defs b/libs/libc/pthread/Make.defs index f9c2de2fbd..bc721a92cd 100644 --- a/libs/libc/pthread/Make.defs +++ b/libs/libc/pthread/Make.defs @@ -63,6 +63,10 @@ ifeq ($(CONFIG_SMP),y) CSRCS += pthread_attr_getaffinity.c pthread_attr_setaffinity.c endif +ifeq ($(CONFIG_PTHREAD_SPINLOCKS),y) +CSRCS += pthread_spinlock.c +endif + ifeq ($(CONFIG_BUILD_PROTECTED),y) CSRCS += pthread_startup.c endif diff --git a/libs/libc/pthread/pthread_spinlock.c b/libs/libc/pthread/pthread_spinlock.c new file mode 100644 index 0000000000..c7447f48eb --- /dev/null +++ b/libs/libc/pthread/pthread_spinlock.c @@ -0,0 +1,307 @@ +/**************************************************************************** + * libs/libc/pthread/pthread_spinlock.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +/* The architecture specific spinlock.h header file must provide the + * following: + * + * SP_LOCKED - A definition of the locked state value (usually 1) + * SP_UNLOCKED - A definition of the unlocked state value (usually 0) + * spinlock_t - The type of a spinlock memory object (usually uint8_t). + * SP_DSB(), - Memory barriers + * SP_DMB + */ + +#include +#include + +#ifdef CONFIG_PTHREAD_SPINLOCKS + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define IMPOSSIBLE_THREAD ((pthread_t)-1) + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pthread_spin_init + * + * Description: + * The pthread_spin_init() function will allocate any resources required + * to use the spin lock referenced by lock and initialize the lock to an + * unlocked state. + * + * The results are undefined if pthread_spin_init() is called specifying + * an already initialized spin lock. The results are undefined if a spin + * lock is used without first being initialized. + * + * If the pthread_spin_init() function fails, the lock is not initialized + * and the contents of lock are undefined. + * + * Only the object referenced by lock may be used for performing + * synchronization. The result of referring to copies of that object in + * calls to pthread_spin_destroy(), pthread_spin_lock(), + * pthread_spin_trylock(), or pthread_spin_unlock() is undefined. + * + * Input Parameters: + * lock - A reference to the spinlock object to be initialized. + * pshared - Unused + * + * Returned Value: + * Zero (OK) is returned if a valid lock value is provided. + * + * POSIX Compatibility: + * Not supported: "If the Thread Process-Shared Synchronization option is + * supported and the value of pshared is PTHREAD_PROCESS_SHARED, the + * implementation shall permit the spin lock to be operated upon by any + * thread that has access to the memory where the spin lock is allocated, + * even if it is allocated in memory that is shared by multiple + * processes." + * + ****************************************************************************/ + +int pthread_spin_init(FAR pthread_spinlock_t *lock, int pshared) +{ + int ret = EINVAL; + + DEBUGASSERT(lock != NULL); + if (lock != NULL) + { + lock->sp_lock = SP_UNLOCKED; + lock->sp_holder = IMPOSSIBLE_THREAD; + ret = OK; + } + + return ret; +} + +/**************************************************************************** + * Name: pthread_spin_destroy + * + * Description: + * The pthread_spin_destroy() function will destroy the spin lock + * referenced by lock and release any resources used by the lock. The + * effect of subsequent use of the lock is undefined until the lock is + * reinitialized by another call to pthread_spin_init(). The results are + * undefined if pthread_spin_destroy() is called when a thread holds the + * lock, or if this function is called with an uninitialized thread spin + * lock. + * + * Input Parameters: + * lock - A reference to the spinlock object to be initialized. + * pshared - Unused + * + * Returned Value: + * Zero (OK) is always returned. + * + * POSIX Compatibility: + * Not supported: "If the Thread Process-Shared Synchronization option is + * supported and the value of pshared is PTHREAD_PROCESS_SHARED, the + * implementation shall permit the spin lock to be operated upon by any + * thread that has access to the memory where the spin lock is allocated, + * even if it is allocated in memory that is shared by multiple + * processes." + * + ****************************************************************************/ + +int pthread_spin_destroy(pthread_spinlock_t *lock) +{ + return OK; +} + +/**************************************************************************** + * Name: pthread_spin_lock + * + * Description: + * The pthread_spin_lock() function will lock the spin lock referenced by + * lock. The calling thread will acquire the lock if it is not held by + * another thread. Otherwise, the thread will spin (that is, will not + * return from the pthread_spin_lock() call) until the lock becomes + * available. + * + * If it is determined the value specified by the lock argument to + * pthread_spin_lock() refers to a spin lock object for which the calling + * thread already holds the lock, it is recommended that the function will + * fail and report an EDEADLK error. + * + * Input Parameters: + * lock - A reference to the spinlock object to lock. + * + * Returned Value: + * Zero is returned if the lock was successfully acquired. Otherwise one + * of the following errno values are retured: + * + * EINVAL - 'lock' does not refer to a valid spinlock object + * EDEADLOCK - The caller already holds the spinlock + * + ****************************************************************************/ + +int pthread_spin_lock(pthread_spinlock_t *lock) +{ + pthread_t me = pthread_self(); + + DEBUGASSERT(lock != NULL); + if (lock == NULL) + { + return EINVAL; + } + else if (lock->sp_holder == me) + { + return EDEADLOCK; + } + + while (up_testset(&lock->sp_lock) == SP_LOCKED) + { + SP_DSB(); + } + + lock->sp_holder = me; + SP_DMB(); + return OK; +} + +/**************************************************************************** + * Name: pthread_spin_trylock + * + * Description: + * The pthread_spin_trylock() function will lock the spin lock referenced + * by lock. The calling thread will acquire the lock if it is not held by + * another thread. Otherwise, The pthread_spin_trylock() will return a + * failure. + * + * Input Parameters: + * lock - A reference to the spinlock object to lock. + * + * Returned Value: + * Zero is returned if the lock was successfully acquired. Otherwise one + * of the following errno values are retured: + * + * EINVAL - 'lock' does not refer to a valid spinlock object + * EBUSY - The spinlock is held by another thread + * + ****************************************************************************/ + +int pthread_spin_trylock(pthread_spinlock_t *lock) +{ + pthread_t me = pthread_self(); + + DEBUGASSERT(lock != NULL); + if (lock == NULL) + { + return EINVAL; + } + else if (lock->sp_holder == me) + { + return OK; + } + else if (up_testset(&lock->sp_lock) == SP_LOCKED) + { + lock->sp_holder = me; + SP_DMB(); + return OK; + } + else + { + return EBUSY; + } +} + +/**************************************************************************** + * Name: pthread_spin_unlock + * + * Description: + * The pthread_spin_unlock() function will release the spin lock + * referenced by lock which was locked via the pthread_spin_lock() or + * pthread_spin_trylock() functions. + * + * The results are undefined if the lock is not held by the calling thread. + * + * If there are threads spinning on the lock when pthread_spin_unlock() is + * called, the lock becomes available and an unspecified spinning thread + * will acquire the lock. + * + * The results are undefined if this function is called with an + * uninitialized thread spin lock. + * + * Input Parameters: + * lock - A reference to the spinlock object to unlock. + * + * Returned Value: + * Zero is returned if the lock was successfully acquired. Otherwise one + * of the following errno values are retured: + * + * EINVAL - 'lock' does not refer to a valid spinlock object + * EDEADLOCK - The caller already holds the spinlock + * + ****************************************************************************/ + +int pthread_spin_unlock(pthread_spinlock_t *lock) +{ + pthread_t me = pthread_self(); + + DEBUGASSERT(lock != NULL && + lock->sp_lock == SP_LOCKED && + lock->sp_holder == me); + + if (lock == NULL) + { + return EINVAL; + } + else if (lock->sp_lock != SP_LOCKED || lock->sp_holder != me) + { + return EPERM; + } + + /* Release the lock */ + + lock->sp_holder = IMPOSSIBLE_THREAD; + lock->sp_lock = SP_UNLOCKED; + return OK; +} + +#endif /* CONFIG_PTHREAD_SPINLOCKS */