From 8b3c554e45aa0cc643f80ae783b3852766a130c7 Mon Sep 17 00:00:00 2001 From: Gregory Nutt <gnutt@nuttx.org> Date: Sun, 26 Mar 2017 17:37:28 -0600 Subject: [PATCH 1/4] pthreads: Add a configuration option to disable robust mutexes and revert to the traditional unsafe mutexes. --- include/nuttx/sched.h | 2 ++ include/pthread.h | 6 ++++++ sched/Kconfig | 20 +++++++++++++++++++ sched/pthread/Make.defs | 7 +++++-- sched/pthread/pthread.h | 6 ++++++ sched/pthread/pthread_cancel.c | 2 ++ sched/pthread/pthread_exit.c | 4 +++- sched/pthread/pthread_mutexinit.c | 7 +++++-- sched/pthread/pthread_mutexlock.c | 21 +++++++++++++------ sched/pthread/pthread_mutextrylock.c | 6 +++++- sched/pthread/pthread_mutexunlock.c | 30 ++++++++++++++++++++++------ 11 files changed, 93 insertions(+), 18 deletions(-) diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h index f618ac94ce..f8bc539e3d 100644 --- a/include/nuttx/sched.h +++ b/include/nuttx/sched.h @@ -699,7 +699,9 @@ struct pthread_tcb_s /* Robust mutex support *******************************************************/ +#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE FAR struct pthread_mutex_s *mhead; /* List of mutexes held by thread */ +#endif /* Clean-up stack *************************************************************/ diff --git a/include/pthread.h b/include/pthread.h index 305bb8db21..214ad454fe 100644 --- a/include/pthread.h +++ b/include/pthread.h @@ -240,15 +240,19 @@ typedef struct pthread_mutexattr_s pthread_mutexattr_t; struct pthread_mutex_s { +#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE /* Supports a singly linked list */ FAR struct pthread_mutex_s *flink; +#endif /* Payload */ sem_t sem; /* Semaphore underlying the implementation of the mutex */ pid_t pid; /* ID of the holder of the mutex */ +#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE uint8_t flags; /* See _PTHREAD_MFLAGS_* */ +#endif #ifdef CONFIG_MUTEX_TYPES uint8_t type; /* Type of the mutex. See PTHREAD_MUTEX_* definitions */ int16_t nlocks; /* The number of recursive locks held */ @@ -438,9 +442,11 @@ int pthread_mutex_lock(FAR pthread_mutex_t *mutex); int pthread_mutex_trylock(FAR pthread_mutex_t *mutex); int pthread_mutex_unlock(FAR pthread_mutex_t *mutex); +#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE /* Make sure that the pthread mutex is in a consistent state */ int pthread_mutex_consistent(FAR pthread_mutex_t *mutex); +#endif /* Operations on condition variables */ diff --git a/sched/Kconfig b/sched/Kconfig index 3a5807d4dc..369e9ac7cd 100644 --- a/sched/Kconfig +++ b/sched/Kconfig @@ -531,6 +531,26 @@ config MUTEX_TYPES: Set to enable support for recursive and errorcheck mutexes. Enables pthread_mutexattr_settype(). +choice + prompt "pthread mutex robustness" + default PTHREAD_MUTEX_ROBUST if !DEFAULT_SMALL + default PTHREAD_UNSAFE if DEFAULT_SMALL + + config PTHREAD_MUTEX_ROBUST + bool "Robust mutexes" + ---help--- + Support only the robust form of the NORMAL mutex. + + config PTHREAD_MUTEX_UNSAFE + bool "Traditional unsafe mutexes" + ---help--- + Support only the traditional non-robust form of the NORMAL mutex. + You should select this option only for backward compatibility with + software you may be porting or, perhaps, if you are trying to minimize + footprint. + +endchoice # thread mutex robustness + config NPTHREAD_KEYS int "Maximum number of pthread keys" default 4 diff --git a/sched/pthread/Make.defs b/sched/pthread/Make.defs index a39a35d208..20bc7e8d6c 100644 --- a/sched/pthread/Make.defs +++ b/sched/pthread/Make.defs @@ -37,9 +37,8 @@ ifneq ($(CONFIG_DISABLE_PTHREAD),y) CSRCS += pthread_create.c pthread_exit.c pthread_join.c pthread_detach.c CSRCS += pthread_yield.c pthread_getschedparam.c pthread_setschedparam.c -CSRCS += pthread_mutexinit.c pthread_mutexdestroy.c pthread_mutex.c +CSRCS += pthread_mutexinit.c pthread_mutexdestroy.c CSRCS += pthread_mutexlock.c pthread_mutextrylock.c pthread_mutexunlock.c -CSRCS += pthread_mutexconsistent.c pthread_mutexinconsistent.c CSRCS += pthread_condinit.c pthread_conddestroy.c CSRCS += pthread_condwait.c pthread_condsignal.c pthread_condbroadcast.c CSRCS += pthread_barrierinit.c pthread_barrierdestroy.c pthread_barrierwait.c @@ -49,6 +48,10 @@ CSRCS += pthread_keydelete.c CSRCS += pthread_initialize.c pthread_completejoin.c pthread_findjoininfo.c CSRCS += pthread_once.c pthread_release.c pthread_setschedprio.c +ifneq ($(CONFIG_PTHREAD_MUTEX_UNSAFE),y) +CSRCS += pthread_mutex.c pthread_mutexconsistent.c pthread_mutexinconsistent.c +endif + ifneq ($(CONFIG_DISABLE_SIGNALS),y) CSRCS += pthread_condtimedwait.c pthread_kill.c pthread_sigmask.c endif diff --git a/sched/pthread/pthread.h b/sched/pthread/pthread.h index 6ab044a3ba..2e7106495f 100644 --- a/sched/pthread/pthread.h +++ b/sched/pthread/pthread.h @@ -109,9 +109,15 @@ FAR struct join_s *pthread_findjoininfo(FAR struct task_group_s *group, void pthread_release(FAR struct task_group_s *group); int pthread_takesemaphore(sem_t *sem, bool intr); int pthread_givesemaphore(sem_t *sem); + +#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE int pthread_mutex_take(FAR struct pthread_mutex_s *mutex, bool intr); int pthread_mutex_give(FAR struct pthread_mutex_s *mutex); void pthread_mutex_inconsistent(FAR struct pthread_tcb_s *tcb); +#else +# define pthread_mutex_take(m,i) pthread_takesemaphore(&(m)->sem,(i)) +# define pthread_mutex_give(m) pthread_givesemaphore(&(m)->sem) +#endif #ifdef CONFIG_MUTEX_TYPES int pthread_mutexattr_verifytype(int type); diff --git a/sched/pthread/pthread_cancel.c b/sched/pthread/pthread_cancel.c index 3a520b1f93..e2cad60ebb 100644 --- a/sched/pthread/pthread_cancel.c +++ b/sched/pthread/pthread_cancel.c @@ -162,9 +162,11 @@ int pthread_cancel(pthread_t thread) (void)pthread_completejoin((pid_t)thread, PTHREAD_CANCELED); +#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE /* Recover any mutexes still held by the canceled thread */ pthread_mutex_inconsistent(tcb); +#endif /* Then let task_terminate do the real work */ diff --git a/sched/pthread/pthread_exit.c b/sched/pthread/pthread_exit.c index 4654d86371..bcc8e79c85 100644 --- a/sched/pthread/pthread_exit.c +++ b/sched/pthread/pthread_exit.c @@ -123,9 +123,11 @@ void pthread_exit(FAR void *exit_value) exit(EXIT_FAILURE); } +#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE /* Recover any mutexes still held by the canceled thread */ - pthread_mutex_inconsistent(tcb); + pthread_mutex_inconsistent((FAR struct pthread_tcb_s *)tcb); +#endif /* Perform common task termination logic. This will get called again later * through logic kicked off by _exit(). However, we need to call it before diff --git a/sched/pthread/pthread_mutexinit.c b/sched/pthread/pthread_mutexinit.c index b521137683..46c9e67356 100644 --- a/sched/pthread/pthread_mutexinit.c +++ b/sched/pthread/pthread_mutexinit.c @@ -123,14 +123,17 @@ int pthread_mutex_init(FAR pthread_mutex_t *mutex, ret = get_errno(); } #endif + +#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE /* Initial internal fields of the mutex */ mutex->flink = NULL; mutex->flags = 0; - - /* Set up attributes unique to the mutex type */ +#endif #ifdef CONFIG_MUTEX_TYPES + /* Set up attributes unique to the mutex type */ + mutex->type = type; mutex->nlocks = 0; #endif diff --git a/sched/pthread/pthread_mutexlock.c b/sched/pthread/pthread_mutexlock.c index 67b668bf9d..ffc8eadad2 100644 --- a/sched/pthread/pthread_mutexlock.c +++ b/sched/pthread/pthread_mutexlock.c @@ -120,13 +120,15 @@ int pthread_mutex_lock(FAR pthread_mutex_t *mutex) sched_lock(); - /* Does this thread already hold the semaphore? */ +#ifdef CONFIG_MUTEX_TYPES + /* All mutex types except for NORMAL (and DEFAULT) will return + * and an error error if the caller does not hold the mutex. + */ - if (mutex->pid == mypid) + if (mutex->type != PTHREAD_MUTEX_NORMAL && mutex->pid == mypid) { /* Yes.. Is this a recursive mutex? */ -#ifdef CONFIG_MUTEX_TYPES if (mutex->type == PTHREAD_MUTEX_RECURSIVE) { /* Yes... just increment the number of locks held and return @@ -144,7 +146,6 @@ int pthread_mutex_lock(FAR pthread_mutex_t *mutex) } } else -#endif { /* No, then we would deadlock... return an error (default * behavior is like PTHREAD_MUTEX_ERRORCHECK) @@ -160,14 +161,17 @@ int pthread_mutex_lock(FAR pthread_mutex_t *mutex) ret = EDEADLK; } } + else +#endif /* CONFIG_MUTEX_TYPES */ +#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE /* The calling thread does not hold the semaphore. The correct * behavior for the 'robust' mutex is to verify that the holder of the * mutex is still valid. This is protection from the case * where the holder of the mutex has exitted without unlocking it. */ - else if (mutex->pid > 0 && sched_gettcb(mutex->pid) == NULL) + if (mutex->pid > 0 && sched_gettcb(mutex->pid) == NULL) { DEBUGASSERT(mutex->pid != 0); /* < 0: available, >0 owned, ==0 error */ DEBUGASSERT((mutex->flags & _PTHREAD_MFLAGS_INCONSISTENT) != 0); @@ -182,8 +186,13 @@ int pthread_mutex_lock(FAR pthread_mutex_t *mutex) ret = EOWNERDEAD; } else +#endif /* !CONFIG_PTHREAD_MUTEX_UNSAFE */ + { - /* Take the underlying semaphore, waiting if necessary */ + /* Take the underlying semaphore, waiting if necessary. NOTE that + * is required to deadlock for the case of the non-robust NORMAL or + * default mutex. + */ ret = pthread_mutex_take(mutex, true); diff --git a/sched/pthread/pthread_mutextrylock.c b/sched/pthread/pthread_mutextrylock.c index 683909c859..6e21c74b44 100644 --- a/sched/pthread/pthread_mutextrylock.c +++ b/sched/pthread/pthread_mutextrylock.c @@ -124,7 +124,7 @@ int pthread_mutex_trylock(FAR pthread_mutex_t *mutex) else { - /* Did it fail because the semaphore was not avaialabl? */ + /* Did it fail because the semaphore was not avaialable? */ int errcode = get_errno(); if (errcode == EAGAIN) @@ -148,6 +148,8 @@ int pthread_mutex_trylock(FAR pthread_mutex_t *mutex) } else #endif + +#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE /* The calling thread does not hold the semaphore. The correct * behavior for the 'robust' mutex is to verify that the holder of * the mutex is still valid. This is protection from the case @@ -172,6 +174,8 @@ int pthread_mutex_trylock(FAR pthread_mutex_t *mutex) /* The mutex is locked by another, active thread */ else +#endif /* CONFIG_PTHREAD_MUTEX_UNSAFE */ + { ret = EBUSY; } diff --git a/sched/pthread/pthread_mutexunlock.c b/sched/pthread/pthread_mutexunlock.c index 7ab0c90469..9efd9ed847 100644 --- a/sched/pthread/pthread_mutexunlock.c +++ b/sched/pthread/pthread_mutexunlock.c @@ -93,9 +93,20 @@ int pthread_mutex_unlock(FAR pthread_mutex_t *mutex) sched_lock(); - /* Does the calling thread own the semaphore? */ +#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 (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. + */ + + if (mutex->type != PTHREAD_MUTEX_NORMAL && mutex->pid != (int)getpid()) +#endif { /* No... return an EPERM error. * @@ -111,11 +122,12 @@ int pthread_mutex_unlock(FAR pthread_mutex_t *mutex) serr("ERROR: Holder=%d returning EPERM\n", mutex->pid); ret = EPERM; } - - /* Yes, the caller owns the semaphore.. Is this a recursive mutex? */ + else #ifdef CONFIG_MUTEX_TYPES - else if (mutex->type == PTHREAD_MUTEX_RECURSIVE && mutex->nlocks > 1) + /* Yes, the caller owns the semaphore.. Is this a recursive mutex? */ + + if (mutex->type == PTHREAD_MUTEX_RECURSIVE && mutex->nlocks > 1) { /* This is a recursive mutex and we there are multiple locks held. Retain * the mutex lock, just decrement the count of locks held, and return @@ -125,13 +137,19 @@ int pthread_mutex_unlock(FAR pthread_mutex_t *mutex) mutex->nlocks--; ret = OK; } -#endif + else + +#endif /* CONFIG_MUTEX_TYPES */ /* This is either a non-recursive mutex or is the outermost unlock of * a recursive mutex. + * + * In the case where the calling thread is NOT the holder of the thread, + * the behavior is undefined per POSIX. Here we do the same as GLIBC: + * We allow the other thread to release the mutex even though it does + * not own it. */ - else { /* Nullify the pid and lock count then post the semaphore */ From 666208cf237248e03ad13a730b8c4f3ea5ccef1a Mon Sep 17 00:00:00 2001 From: Gregory Nutt <gnutt@nuttx.org> Date: Sun, 26 Mar 2017 18:37:24 -0600 Subject: [PATCH 2/4] pthread mutexes: Add option to support both unsafe and robust mutexes via pthread_mutexattr_get/setrobust(). --- include/pthread.h | 47 ++++++++++++++++++++++++--- libc/pthread/Make.defs | 1 + libc/pthread/pthread_mutexattr_init.c | 10 ++++++ sched/Kconfig | 24 +++++++++++++- sched/pthread/pthread_mutexinit.c | 12 ++++++- 5 files changed, 87 insertions(+), 7 deletions(-) diff --git a/include/pthread.h b/include/pthread.h index 214ad454fe..30c96bab9c 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_NOTRECOVRABLE (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 089845d8f7..b8be2927d2 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_init.c b/libc/pthread/pthread_mutexattr_init.c index f1b6bca9c7..de6183e56c 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/sched/Kconfig b/sched/Kconfig index 369e9ac7cd..60183e67d2 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_mutexinit.c b/sched/pthread/pthread_mutexinit.c index 46c9e67356..e6abe32900 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 From d1196ddb6072056c931f1f187ab20148dbebe943 Mon Sep 17 00:00:00 2001 From: Gregory Nutt <gnutt@nuttx.org> Date: Mon, 27 Mar 2017 07:49:13 -0600 Subject: [PATCH 3/4] pthread mutex: Forgot to add files in last commit --- libc/pthread/pthread_mutexattr_getrobust.c | 82 ++++++++++++++++++ libc/pthread/pthread_mutexattr_setrobust.c | 96 ++++++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 libc/pthread/pthread_mutexattr_getrobust.c create mode 100644 libc/pthread/pthread_mutexattr_setrobust.c diff --git a/libc/pthread/pthread_mutexattr_getrobust.c b/libc/pthread/pthread_mutexattr_getrobust.c new file mode 100644 index 0000000000..8d5e4c1060 --- /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 <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 <pthread.h> +#include <errno.h> + +/**************************************************************************** + * 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_setrobust.c b/libc/pthread/pthread_mutexattr_setrobust.c new file mode 100644 index 0000000000..42930fa0b7 --- /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 <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 <pthread.h> +#include <errno.h> + +/**************************************************************************** + * 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 +} From f2f798cb2957582ab61d100aa0ab9b3680be24e8 Mon Sep 17 00:00:00 2001 From: Gregory Nutt <gnutt@nuttx.org> Date: Mon, 27 Mar 2017 08:50:45 -0600 Subject: [PATCH 4/4] pthread mutexes: Finish logic to support configuration mutex robustness. --- include/pthread.h | 2 +- sched/pthread/pthread_mutexconsistent.c | 8 ++-- sched/pthread/pthread_mutexlock.c | 21 ++++++++++ sched/pthread/pthread_mutextrylock.c | 21 ++++++++++ sched/pthread/pthread_mutexunlock.c | 53 +++++++++++++++++++------ 5 files changed, 88 insertions(+), 17 deletions(-) diff --git a/include/pthread.h b/include/pthread.h index 30c96bab9c..53eaa0ddaa 100644 --- a/include/pthread.h +++ b/include/pthread.h @@ -178,7 +178,7 @@ #define _PTHREAD_MFLAGS_ROBUST (1 << 0) /* Robust (NORMAL) mutex */ #define _PTHREAD_MFLAGS_INCONSISTENT (1 << 1) /* Mutex is in an inconsistent state */ -#define _PTHREAD_MFLAGS_NOTRECOVRABLE (1 << 2) /* Inconsistent mutex has been unlocked */ +#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 diff --git a/sched/pthread/pthread_mutexconsistent.c b/sched/pthread/pthread_mutexconsistent.c index 3febab2d0b..86930e276c 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_mutexlock.c b/sched/pthread/pthread_mutexlock.c index ffc8eadad2..3013248842 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 6e21c74b44..50c3175de5 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 9efd9ed847..b27c9ecda0 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; }