include/pthread.h and libs/libc/pthread/pthread_spinlock.c: Add a very preliminary implementation of the new POSIX pthread_spinlock_* interfaces. This feature is marked EXPERIMENTAL. There are usage model issues to be examined and there are also architectural issues that currently limit the implemantion to the FLAT build (that is, however, fixable when the APIs are needed.

This commit is contained in:
Gregory Nutt 2019-02-28 10:21:05 -06:00
parent 6d0d712b9c
commit b2f110e0b0
6 changed files with 393 additions and 0 deletions

View File

@ -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 */

View File

@ -55,6 +55,20 @@
#include <nuttx/semaphore.h> /* 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 <arch/spinlock.h>
#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

View File

@ -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

26
libs/libc/pthread/Kconfig Normal file
View File

@ -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

View File

@ -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

View File

@ -0,0 +1,307 @@
/****************************************************************************
* libs/libc/pthread/pthread_spinlock.c
*
* Copyright (C) 2019 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* 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 <nuttx/config.h>
#include <sys/types.h>
#include <pthread.h>
#include <sched.h>
/* 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 <nuttx/spinlock.h>
#include <arch/spinlock.h>
#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 */