diff --git a/include/pthread.h b/include/pthread.h index 214ad454fe9..53eaa0ddaa1 100644 --- a/include/pthread.h +++ b/include/pthread.h @@ -143,12 +143,42 @@ #define PTHREAD_PRIO_INHERIT SEM_PRIO_INHERIT #define PTHREAD_PRIO_PROTECT SEM_PRIO_PROTECT +/* Values for robust argument of pthread_mutexattr_get/setrobust + * + * PTHREAD_MUTEX_STALLED - No special actions are taken if the owner of the + * mutex is terminated while holding the mutex lock. This can lead to + * deadlocks if no other thread can unlock the mutex. This is the standard + * default value (NuttX permits you to override that default behavior + * with a configuration option). + * + * PTHREAD_MUTEX_ROBUST - If the process containing the owning thread of a + * robust mutex terminates while holding the mutex lock, the next thread + * that acquires the mutex will be notified about the termination by the + * return value EOWNERDEAD from the locking function. If the owning thread + * of a robust mutex terminates while holding the mutex lock, the next + * thread that attempts to acquire the mutex may be notified about the + * termination by the return value EOWNERDEAD. The notified thread can + * then attempt to make the state protected by the mutex consistent again, + * and if successful can mark the mutex state as consistent by calling + * pthread_mutex_consistent(). After a subsequent successful call to + * pthread_mutex_unlock(), the mutex lock will be released and can be used + * normally by other threads. If the mutex is unlocked without a call to + * pthread_mutex_consistent(), it will be in a permanently unusable state + * and all attempts to lock the mutex will fail with the error + * ENOTRECOVERABLE. The only permissible operation on such a mutex is + * pthread_mutex_destroy(). + */ + +#define PTHREAD_MUTEX_STALLED 0 +#define PTHREAD_MUTEX_ROBUST 1 + /* Values for struct pthread_mutex_s flags. These are non-standard and * intended only for internal use within the OS. */ -#define _PTHREAD_MFLAGS_INCONSISTENT (1 << 0) /* Mutex is in an inconsistent state */ -#define _PTHREAD_MFLAGS_NOTRECOVRABLE (1 << 1) /* Inconsistent mutex has been unlocked */ +#define _PTHREAD_MFLAGS_ROBUST (1 << 0) /* Robust (NORMAL) mutex */ +#define _PTHREAD_MFLAGS_INCONSISTENT (1 << 1) /* Mutex is in an inconsistent state */ +#define _PTHREAD_MFLAGS_NRECOVERABLE (1 << 2) /* Inconsistent mutex has been unlocked */ /* Definitions to map some non-standard, BSD thread management interfaces to * the non-standard Linux-like prctl() interface. Since these are simple @@ -226,12 +256,15 @@ typedef struct pthread_cond_s pthread_cond_t; struct pthread_mutexattr_s { - uint8_t pshared; /* PTHREAD_PROCESS_PRIVATE or PTHREAD_PROCESS_SHARED */ + uint8_t pshared : 1; /* PTHREAD_PROCESS_PRIVATE or PTHREAD_PROCESS_SHARED */ #ifdef CONFIG_PRIORITY_INHERITANCE - uint8_t proto; /* See PTHREAD_PRIO_* definitions */ + uint8_t proto : 2; /* See PTHREAD_PRIO_* definitions */ #endif #ifdef CONFIG_MUTEX_TYPES - uint8_t type; /* Type of the mutex. See PTHREAD_MUTEX_* definitions */ + uint8_t type : 2; /* Type of the mutex. See PTHREAD_MUTEX_* definitions */ +#endif +#ifdef CONFIG_PTHREAD_MUTEX_BOTH + uint8_t robust : 1; /* PTHREAD_MUTEX_STALLED or PTHREAD_MUTEX_ROBUST */ #endif }; @@ -432,6 +465,10 @@ int pthread_mutexattr_getprotocol(FAR const pthread_mutexattr_t *attr, FAR int *protocol); int pthread_mutexattr_setprotocol(FAR pthread_mutexattr_t *attr, int protocol); +int pthread_mutexattr_getrobust(FAR const pthread_mutexattr_t *attr, + FAR int *robust); +int pthread_mutexattr_setrobust(FAR pthread_mutexattr_t *attr, + int robust); /* The following routines create, delete, lock and unlock mutexes. */ diff --git a/libc/pthread/Make.defs b/libc/pthread/Make.defs index 089845d8f7e..b8be2927d2f 100644 --- a/libc/pthread/Make.defs +++ b/libc/pthread/Make.defs @@ -49,6 +49,7 @@ CSRCS += pthread_mutexattr_init.c pthread_mutexattr_destroy.c CSRCS += pthread_mutexattr_getpshared.c pthread_mutexattr_setpshared.c CSRCS += pthread_mutexattr_setprotocol.c pthread_mutexattr_getprotocol.c CSRCS += pthread_mutexattr_settype.c pthread_mutexattr_gettype.c +CSRCS += pthread_mutexattr_setrobust.c pthread_mutexattr_getrobust.c CSRCS += pthread_setcancelstate.c pthread_setcanceltype.c CSRCS += pthread_testcancel.c diff --git a/libc/pthread/pthread_mutexattr_getrobust.c b/libc/pthread/pthread_mutexattr_getrobust.c new file mode 100644 index 00000000000..8d5e4c10607 --- /dev/null +++ b/libc/pthread/pthread_mutexattr_getrobust.c @@ -0,0 +1,82 @@ +/**************************************************************************** + * libc/pthread/pthread_mutexattr_getrobust.c + * + * Copyright (C) 2017 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 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: pthread_mutexattr_getrobust + * + * Description: + * Return the mutex robustneess from the mutex attributes. + * + * Parameters: + * attr - The mutex attributes to query + * robust - Location to return the robustness indication + * + * Return Value: + * 0, if the robustness was successfully return in 'robust', or + * EINVAL, if any NULL pointers provided. + * + * Assumptions: + * + ****************************************************************************/ + +int pthread_mutexattr_getrobust(FAR const pthread_mutexattr_t *attr, + FAR int *robust) +{ + if (attr != NULL && robust != NULL) + { +#if defined(CONFIG_PTHREAD_MUTEX_UNSAFE) + *robust = PTHREAD_MUTEX_STALLED; +#elif defined(CONFIG_PTHREAD_MUTEX_BOTH) + *robust = attr->robust; +#else /* Default: CONFIG_PTHREAD_MUTEX_ROBUST */ + *robust = PTHREAD_MUTEX_ROBUST; +#endif + return 0; + } + + return EINVAL; +} diff --git a/libc/pthread/pthread_mutexattr_init.c b/libc/pthread/pthread_mutexattr_init.c index f1b6bca9c7c..de6183e56c3 100644 --- a/libc/pthread/pthread_mutexattr_init.c +++ b/libc/pthread/pthread_mutexattr_init.c @@ -76,11 +76,21 @@ int pthread_mutexattr_init(FAR pthread_mutexattr_t *attr) else { attr->pshared = 0; + #ifdef CONFIG_PRIORITY_INHERITANCE attr->proto = SEM_PRIO_INHERIT; #endif + #ifdef CONFIG_MUTEX_TYPES attr->type = PTHREAD_MUTEX_DEFAULT; +#endif + +#ifdef CONFIG_PTHREAD_MUTEX_BOTH +#ifdef CONFIG_PTHREAD_MUTEX_DEFAULT_UNSAFE + attr->robust = PTHREAD_MUTEX_STALLED; +#else + attr->robust = PTHREAD_MUTEX_ROBUST; +#endif #endif } diff --git a/libc/pthread/pthread_mutexattr_setrobust.c b/libc/pthread/pthread_mutexattr_setrobust.c new file mode 100644 index 00000000000..42930fa0b73 --- /dev/null +++ b/libc/pthread/pthread_mutexattr_setrobust.c @@ -0,0 +1,96 @@ +/**************************************************************************** + * libc/pthread/pthread_mutexattr_setrobust.c + * + * Copyright (C) 201t 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 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: pthread_mutexattr_setrobust + * + * Description: + * Set the mutex robustness in the mutex attributes. + * + * Parameters: + * attr - The mutex attributes in which to set the mutex type. + * robust - The mutex type value to set. + * + * Return Value: + * 0, if the mutex robustness was successfully set in 'attr', or + * EINVAL, if 'attr' is NULL or 'robust' unrecognized. + * + * Assumptions: + * + ****************************************************************************/ + +int pthread_mutexattr_setrobust(pthread_mutexattr_t *attr, int robust) +{ +#if defined(CONFIG_PTHREAD_MUTEX_UNSAFE) + + if (attr != NULL && robust == PTHREAD_MUTEX_STALLED) + { + return OK; + } + + return EINVAL; + +#elif defined(CONFIG_PTHREAD_MUTEX_BOTH) + + if (attr != NULL && (robust == PTHREAD_MUTEX_STALLED || robust == _PTHREAD_MFLAGS_ROBUST)) + { + attr->robust = robust; + return OK; + } + + return EINVAL; + +#else /* Default: CONFIG_PTHREAD_MUTEX_ROBUST */ + + if (attr != NULL && robust == _PTHREAD_MFLAGS_ROBUST) + { + return OK; + } + + return EINVAL; +#endif +} diff --git a/sched/Kconfig b/sched/Kconfig index 369e9ac7cd7..60183e67d2c 100644 --- a/sched/Kconfig +++ b/sched/Kconfig @@ -549,7 +549,29 @@ choice software you may be porting or, perhaps, if you are trying to minimize footprint. -endchoice # thread mutex robustness + config PTHREAD_MUTEX_BOTH + bool "Both robust and unsafe mutexes" + ---help--- + Support both forms of NORMAL mutexes. + +endchoice # pthread mutex robustness + +choice + prompt "Default NORMAL mutex robustness" + default PTHREAD_MUTEX_DEFAULT_ROBUST + depends on PTHREAD_MUTEX_BOTH + + config PTHREAD_MUTEX_DEFAULT_ROBUST + bool "Robust default" + ---help--- + The default is robust NORMAL mutexes (non-standard) + + config PTHREAD_MUTEX_DEFAULT_UNSAFE + bool "Unsafe default" + ---help--- + The default is traditional unsafe NORMAL mutexes (standard) + +endchoice # Default NORMAL mutex robustness config NPTHREAD_KEYS int "Maximum number of pthread keys" diff --git a/sched/pthread/pthread_mutexconsistent.c b/sched/pthread/pthread_mutexconsistent.c index 3febab2d0b0..86930e276cf 100644 --- a/sched/pthread/pthread_mutexconsistent.c +++ b/sched/pthread/pthread_mutexconsistent.c @@ -118,9 +118,11 @@ int pthread_mutex_consistent(FAR pthread_mutex_t *mutex) { /* The thread associated with the PID no longer exists */ - mutex->pid = -1; - mutex->flags = 0; - + mutex->pid = -1; + mutex->flags &= _PTHREAD_MFLAGS_ROBUST; +#ifdef CONFIG_MUTEX_TYPES + mutex->nlocks = 0; +#endif /* Reset the semaphore. This has the same affect as if the * dead task had called pthread_mutex_unlock(). */ diff --git a/sched/pthread/pthread_mutexinit.c b/sched/pthread/pthread_mutexinit.c index 46c9e67356b..e6abe32900f 100644 --- a/sched/pthread/pthread_mutexinit.c +++ b/sched/pthread/pthread_mutexinit.c @@ -77,6 +77,13 @@ int pthread_mutex_init(FAR pthread_mutex_t *mutex, #endif #ifdef CONFIG_PRIORITY_INHERITANCE uint8_t proto = PTHREAD_PRIO_INHERIT; +#endif +#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE +#ifdef CONFIG_PTHREAD_MUTEX_DEFAULT_UNSAFE + uint8_t robust = PTHREAD_MUTEX_STALLED; +#else + uint8_t robust = PTHREAD_MUTEX_ROBUST; +#endif #endif int ret = OK; int status; @@ -99,6 +106,9 @@ int pthread_mutex_init(FAR pthread_mutex_t *mutex, #endif #ifdef CONFIG_MUTEX_TYPES type = attr->type; +#endif +#ifdef CONFIG_PTHREAD_MUTEX_BOTH + robust = attr->robust; #endif } @@ -128,7 +138,7 @@ int pthread_mutex_init(FAR pthread_mutex_t *mutex, /* Initial internal fields of the mutex */ mutex->flink = NULL; - mutex->flags = 0; + mutex->flags = (robust == PTHREAD_MUTEX_ROBUST ? _PTHREAD_MFLAGS_ROBUST : 0); #endif #ifdef CONFIG_MUTEX_TYPES diff --git a/sched/pthread/pthread_mutexlock.c b/sched/pthread/pthread_mutexlock.c index ffc8eadad2e..30132488424 100644 --- a/sched/pthread/pthread_mutexlock.c +++ b/sched/pthread/pthread_mutexlock.c @@ -171,7 +171,28 @@ int pthread_mutex_lock(FAR pthread_mutex_t *mutex) * where the holder of the mutex has exitted without unlocking it. */ +#ifdef CONFIG_PTHREAD_MUTEX_BOTH +#ifdef CONFIG_MUTEX_TYPES + /* Include check if this is a NORMAL mutex and that it is robust */ + + if (mutex->pid > 0 && + ((mutex->flags & _PTHREAD_MFLAGS_ROBUST) != 0 || + mutex->type != PTHREAD_MUTEX_NORMAL) && + sched_gettcb(mutex->pid) == NULL) + +#else /* CONFIG_MUTEX_TYPES */ + /* This can only be a NORMAL mutex. Include check if it is robust */ + + if (mutex->pid > 0 && + (mutex->flags & _PTHREAD_MFLAGS_ROBUST) != 0 && + sched_gettcb(mutex->pid) == NULL) + +#endif /* CONFIG_MUTEX_TYPES */ +#else /* CONFIG_PTHREAD_MUTEX_ROBUST */ + /* This mutex is always robust, whatever type it is. */ + if (mutex->pid > 0 && sched_gettcb(mutex->pid) == NULL) +#endif { DEBUGASSERT(mutex->pid != 0); /* < 0: available, >0 owned, ==0 error */ DEBUGASSERT((mutex->flags & _PTHREAD_MFLAGS_INCONSISTENT) != 0); diff --git a/sched/pthread/pthread_mutextrylock.c b/sched/pthread/pthread_mutextrylock.c index 6e21c74b44e..50c3175de58 100644 --- a/sched/pthread/pthread_mutextrylock.c +++ b/sched/pthread/pthread_mutextrylock.c @@ -156,7 +156,28 @@ int pthread_mutex_trylock(FAR pthread_mutex_t *mutex) * where the holder of the mutex has exitted without unlocking it. */ +#ifdef CONFIG_PTHREAD_MUTEX_BOTH +#ifdef CONFIG_MUTEX_TYPES + /* Check if this NORMAL mutex is robust */ + + if (mutex->pid > 0 && + ((mutex->flags & _PTHREAD_MFLAGS_ROBUST) != 0 || + mutex->type != PTHREAD_MUTEX_NORMAL) && + sched_gettcb(mutex->pid) == NULL) + +#else /* CONFIG_MUTEX_TYPES */ + /* Check if this NORMAL mutex is robust */ + + if (mutex->pid > 0 && + (mutex->flags & _PTHREAD_MFLAGS_ROBUST) != 0 && + sched_gettcb(mutex->pid) == NULL) + +#endif /* CONFIG_MUTEX_TYPES */ +#else /* CONFIG_PTHREAD_MUTEX_ROBUST */ + /* This mutex is always robust, whatever type it is. */ + if (mutex->pid > 0 && sched_gettcb(mutex->pid) == NULL) +#endif { DEBUGASSERT(mutex->pid != 0); /* < 0: available, >0 owned, ==0 error */ DEBUGASSERT((mutex->flags & _PTHREAD_MFLAGS_INCONSISTENT) != 0); diff --git a/sched/pthread/pthread_mutexunlock.c b/sched/pthread/pthread_mutexunlock.c index 9efd9ed8475..b27c9ecda0c 100644 --- a/sched/pthread/pthread_mutexunlock.c +++ b/sched/pthread/pthread_mutexunlock.c @@ -85,27 +85,54 @@ int pthread_mutex_unlock(FAR pthread_mutex_t *mutex) sinfo("mutex=0x%p\n", mutex); DEBUGASSERT(mutex != NULL); + /* Make sure the semaphore is stable while we make the following checks. + * This all needs to be one atomic action. + */ + + sched_lock(); if (mutex != NULL) { - /* Make sure the semaphore is stable while we make the following - * checks. This all needs to be one atomic action. +#if !defined(CONFIG_PTHREAD_MUTEX_UNSAFE) || defined(CONFIG_MUTEX_TYPES) + /* Does the calling thread own the semaphore? If no, should we return + * an error? + * + * Error checking is always performed for ERRORCHECK and RECURSIVE + * mutex types. Error checking is only performed for NORMAL (or + * DEFAULT) mutex type if the NORMAL mutex is robust. That is either: + * + * 1. CONFIG_PTHREAD_MUTEX_ROBUST is defined, or + * 2. CONFIG_PTHREAD_MUTEX_BOTH is defined and the robust flag is set */ - sched_lock(); - -#if !defined(CONFIG_PTHREAD_MUTEX_UNSAFE) || !defined(CONFIG_MUTEX_TYPES) - /* Does the calling thread own the semaphore? Should we report the - * EPERM error? This applies to robust NORMAL (and DEFAULT) mutexes - * as well as ERRORCHECK and RECURSIVE mutexes. +#if defined(CONFIG_PTHREAD_MUTEX_ROBUST) + /* Not that error checking is always performed if the configuration has + * CONFIG_PTHREAD_MUTEX_ROBUST defined. Just check if the calling + * thread owns the semaphore. */ if (mutex->pid != (int)getpid()) -#else - /* Does the calling thread own the semaphore? Should we report the - * EPERM error? This applies to ERRORCHECK and RECURSIVE mutexes. + +#elif defined(CONFIG_PTHREAD_MUTEX_UNSAFE) && defined(CONFIG_MUTEX_TYPES) + /* If mutex types are not supported, then all mutexes are NORMAL (or + * DEFAULT). Error checking should never be performed for the + * non-robust NORMAL mutex type. */ if (mutex->type != PTHREAD_MUTEX_NORMAL && mutex->pid != (int)getpid()) + +#else /* CONFIG_PTHREAD_MUTEX_BOTH */ + /* Skip the error check if this is a non-robust NORMAL mutex */ + + bool errcheck = ((mutex->flags & _PTHREAD_MFLAGS_ROBUST) != 0); +#ifdef CONFIG_MUTEX_TYPES + errcheck |= (mutex->type != PTHREAD_MUTEX_NORMAL); +#endif + + /* Does the calling thread own the semaphore? If not should we report + * the EPERM error? + */ + + if (errcheck && mutex->pid != (int)getpid()) #endif { /* No... return an EPERM error. @@ -123,6 +150,7 @@ int pthread_mutex_unlock(FAR pthread_mutex_t *mutex) ret = EPERM; } else +#endif /* !CONFIG_PTHREAD_MUTEX_UNSAFE || CONFIG_MUTEX_TYPES */ #ifdef CONFIG_MUTEX_TYPES /* Yes, the caller owns the semaphore.. Is this a recursive mutex? */ @@ -159,10 +187,9 @@ int pthread_mutex_unlock(FAR pthread_mutex_t *mutex) #endif ret = pthread_mutex_give(mutex); } - - sched_unlock(); } + sched_unlock(); sinfo("Returning %d\n", ret); return ret; }