pthread: backport pthread mutex related patches
authorEunBong Song <eunb.song@samsung.com>
Thu, 13 Apr 2017 03:55:50 +0000 (12:55 +0900)
committerHeesub Shin <heesub.shin@samsung.com>
Tue, 18 Apr 2017 03:02:19 +0000 (12:02 +0900)
This patch backports several commits from NuttX, that fixes pthread
mutex issues. Applying only one patch causes compilation errors. So
these patches are submitted in a single commit. All credits should go to
Gregory Nutt who wrote the original commits.

Change-Id: I5dfb4f68a253228fa55335dd6dd4e4413c690f45
Signed-off-by: Gregory Nutt <gnutt@nuttx.org>
[Song: backported a1fbc2ad2c37d3696e623ce0fe03ef025a69453e34c5e1c18b23c16b86ab384d and 8b3c554e. from NuttX]
Signed-off-by: EunBong Song <eunb.song@samsung.com>
15 files changed:
os/include/tinyara/sched.h
os/kernel/pthread/Make.defs
os/kernel/pthread/pthread.h
os/kernel/pthread/pthread_cancel.c
os/kernel/pthread/pthread_condtimedwait.c
os/kernel/pthread/pthread_condwait.c
os/kernel/pthread/pthread_exit.c
os/kernel/pthread/pthread_mutex.c [new file with mode: 0644]
os/kernel/pthread/pthread_mutexconsistent.c
os/kernel/pthread/pthread_mutexinconsistent.c [new file with mode: 0644]
os/kernel/pthread/pthread_mutexinit.c
os/kernel/pthread/pthread_mutexlock.c
os/kernel/pthread/pthread_mutextrylock.c
os/kernel/pthread/pthread_mutexunlock.c
os/kernel/sched/sched.h

index 71923e0..56842de 100644 (file)
@@ -629,6 +629,14 @@ struct pthread_tcb_s {
        pthread_addr_t arg;                     /* Startup argument                    */
        FAR void *joininfo;                     /* Detach-able info to support join    */
 
+       /* Robust mutex support *********************************************/
+
+#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE
+       FAR struct pthread_mutex_s *mhead;      /* List of mutexes held by thread      */
+#endif
+
+       /* Clean-up stack ***************************************************/
+
 #ifdef CONFIG_PTHREAD_CLEANUP
        /* tos   - The index to the next avaiable entry at the top of the stack.
         * stack - The pre-allocated clean-up stack memory.
index 9e4d55b..389754a 100644 (file)
@@ -56,7 +56,6 @@ 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
 CSRCS += pthread_mutexlock.c pthread_mutextrylock.c pthread_mutexunlock.c
-CSRCS += pthread_mutexconsistent.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
@@ -65,6 +64,10 @@ CSRCS += pthread_keycreate.c pthread_setspecific.c pthread_getspecific.c pthread
 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
index ae7cd33..5cc548c 100644 (file)
@@ -121,8 +121,17 @@ int pthread_completejoin(pid_t pid, FAR void *exit_value);
 void pthread_destroyjoin(FAR struct task_group_s *group, FAR struct join_s *pjoin);
 FAR struct join_s *pthread_findjoininfo(FAR struct task_group_s *group, pid_t pid);
 void pthread_release(FAR struct task_group_s *group);
-int pthread_givesemaphore(sem_t *sem);
 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);
index 240f2b4..8bd9d95 100644 (file)
@@ -95,7 +95,7 @@
 
 int pthread_cancel(pthread_t thread)
 {
-       struct pthread_tcb_s *tcb;
+       FAR struct pthread_tcb_s *tcb;
 
        /* First, make sure that the handle references a valid thread */
 
@@ -108,12 +108,15 @@ int pthread_cancel(pthread_t thread)
 
        tcb = (FAR struct pthread_tcb_s *)sched_gettcb((pid_t)thread);
        if (!tcb) {
+       
                /* The pid does not correspond to any known thread.  The thread
                 * has probably already exited.
                 */
                return ESRCH;
        }
 
+       DEBUGASSERT((tcb->cmn.flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD);
+
        /* Check to see if this thread has the non-cancelable bit set in its
         * flags. Suppress context changes for a bit so that the flags are stable.
         * (the flags should not change in interrupt handling.
@@ -166,7 +169,7 @@ int pthread_cancel(pthread_t thread)
         * same as pthread_exit(PTHREAD_CANCELED).
         */
 
-       if (tcb == (struct pthread_tcb_s *)g_readytorun.head) {
+       if (tcb == (FAR struct pthread_tcb_s *)g_readytorun.head) {
                pthread_exit(PTHREAD_CANCELED);
        }
 
@@ -187,6 +190,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 */
 
        return task_terminate((pid_t)thread, false);
index b9f6be9..b951cee 100644 (file)
@@ -274,7 +274,7 @@ int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex,
                                        /* Give up the mutex */
 
                                        mutex->pid = -1;
-                                       ret = pthread_givesemaphore((sem_t *)&mutex->sem);
+                                       ret = pthread_mutex_give(mutex);
                                        if (ret != 0) {
                                                /* Restore interrupts  (pre-emption will be enabled when
                                                 * we fall through the if/then/else)
@@ -322,7 +322,7 @@ int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex,
                                        /* Reacquire the mutex (retaining the ret). */
 
                                        svdbg("Re-locking...\n");
-                                       status = pthread_takesemaphore((FAR sem_t *)&mutex->sem, false);
+                                       status = pthread_mutex_take(mutex, false);
                                        if (status == OK) {
                                                mutex->pid = mypid;
                                        } else if (ret == 0) {
index 40837d5..b6002c6 100644 (file)
@@ -132,11 +132,11 @@ int pthread_cond_wait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex)
 
                sched_lock();
                mutex->pid = -1;
-               ret = pthread_givesemaphore((sem_t *)&mutex->sem);
+               ret = pthread_mutex_give(mutex);
 
                /* Take the semaphore */
 
-               status = pthread_takesemaphore((FAR sem_t *)&cond->sem, false);
+               status = pthread_mutex_take(mutex, false);
                if (ret == OK) {
                        /* Report the first failure that occurs */
 
index b2eb058..00d6535 100644 (file)
@@ -153,6 +153,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((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/os/kernel/pthread/pthread_mutex.c b/os/kernel/pthread/pthread_mutex.c
new file mode 100644 (file)
index 0000000..a13f2ac
--- /dev/null
@@ -0,0 +1,343 @@
+/****************************************************************************
+ *
+ * Copyright 2016 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * kernel/pthread/pthread_mutex.c
+ *
+ *   Copyright (C) 2007-2009 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 <tinyara/config.h>
+
+#include <stdbool.h>
+#include <sched.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <tinyara/irq.h>
+#include <tinyara/sched.h>
+
+#include "sched/sched.h"
+#include "pthread/pthread.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pthread_mutex_add
+ *
+ * Description:
+ *   Add the mutex to the list of mutexes held by this thread.
+ *
+ * Parameters:
+ *  mutex - The mux to be locked
+ *
+ * Return Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void pthread_mutex_add(FAR struct pthread_mutex_s *mutex)
+{
+       FAR struct pthread_tcb_s *rtcb = (FAR struct pthread_tcb_s *)this_task();
+       irqstate_t flags;
+
+       DEBUGASSERT(mutex->flink == NULL);
+
+       /* Add the mutex to the list of mutexes held by this task */
+
+       flags = irqsave();
+       mutex->flink = rtcb->mhead;
+       rtcb->mhead = mutex;
+       irqrestore(flags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pthread_mutex_take
+ *
+ * Description:
+ *   Take the pthread_mutex, waiting if necessary.  If successful, add the
+ *   mutex to the list of mutexes held by this thread.
+ *
+ * Parameters:
+ *  mutex - The mutex to be locked
+ *  intr  - false: ignore EINTR errors when locking; true treat EINTR as
+ *          other errors by returning the errno value
+ *
+ * Return Value:
+ *   0 on success or an errno value on failure.
+ *
+ ****************************************************************************/
+
+int pthread_mutex_take(FAR struct pthread_mutex_s *mutex, bool intr)
+{
+       int ret = EINVAL;
+
+       /* Verify input parameters */
+
+       DEBUGASSERT(mutex != NULL);
+       if (mutex != NULL) {
+               /* Make sure that no unexpected context switches occur */
+
+               sched_lock();
+
+               /* Error out if the mutex is already in an inconsistent state. */
+
+               if ((mutex->flags & _PTHREAD_MFLAGS_INCONSISTENT) != 0) {
+                       ret = EOWNERDEAD;
+               } else {
+                       /* Take semaphore underlying the mutex.  pthread_takesemaphore
+                        * returns zero on success and a positive errno value on failure.
+                        */
+
+                       ret = pthread_takesemaphore(&mutex->sem, intr);
+                       if (ret == OK) {
+                               /* Check if the holder of the mutex has terminated without
+                                * releasing.  In that case, the state of the mutex is
+                                * inconsistent and we return EOWNERDEAD.
+                                */
+
+                               if ((mutex->flags & _PTHREAD_MFLAGS_INCONSISTENT) != 0) {
+                                       ret = EOWNERDEAD;
+                               }
+
+                               /* Add the mutex to the list of mutexes held by this task */
+
+                               else {
+                                       pthread_mutex_add(mutex);
+                               }
+                       }
+               }
+
+               sched_unlock();
+       }
+
+       return ret;
+}
+
+/****************************************************************************
+ * Name: pthread_mutex_trytake
+ *
+ * Description:
+ *   Try to take the pthread_mutex without waiting.  If successful, add the
+ *   mutex to the list of mutexes held by this thread.
+ *
+ * Parameters:
+ *  mutex - The mutex to be locked
+ *  intr  - false: ignore EINTR errors when locking; true treat EINTR as
+ *          other errors by returning the errno value
+ *
+ * Return Value:
+ *   0 on success or an errno value on failure.
+ *
+ ****************************************************************************/
+
+int pthread_mutex_trytake(FAR struct pthread_mutex_s *mutex)
+{
+       int ret = EINVAL;
+
+       /* Verify input parameters */
+
+       DEBUGASSERT(mutex != NULL);
+       if (mutex != NULL) {
+               /* Make sure that no unexpected context switches occur */
+
+               sched_lock();
+
+               /* Error out if the mutex is already in an inconsistent state. */
+
+               if ((mutex->flags & _PTHREAD_MFLAGS_INCONSISTENT) != 0) {
+                       ret = EOWNERDEAD;
+               } else {
+                       /* Try to take the semaphore underlying the mutex */
+
+                       ret = sem_trywait(&mutex->sem);
+                       if (ret < OK) {
+                               ret = get_errno();
+                       } else {
+                               /* Add the mutex to the list of mutexes held by this task */
+
+                               pthread_mutex_add(mutex);
+                       }
+               }
+
+               sched_unlock();
+       }
+
+       return ret;
+}
+
+/****************************************************************************
+ * Name: pthread_mutex_give
+ *
+ * Description:
+ *   Take the pthread_mutex and, if successful, add the mutex to the ist of
+ *   mutexes held by this thread.
+ *
+ * Parameters:
+ *  mutex - The mux to be unlocked
+ *
+ * Return Value:
+ *   0 on success or an errno value on failure.
+ *
+ ****************************************************************************/
+
+int pthread_mutex_give(FAR struct pthread_mutex_s *mutex)
+{
+       FAR struct pthread_mutex_s *curr;
+       FAR struct pthread_mutex_s *prev;
+       int ret = EINVAL;
+
+       /* Verify input parameters */
+
+       DEBUGASSERT(mutex != NULL);
+       if (mutex != NULL) {
+               FAR struct pthread_tcb_s *rtcb = (FAR struct pthread_tcb_s *)this_task();
+               irqstate_t flags;
+
+               flags = irqsave();
+
+               /* Remove the mutex from the list of mutexes held by this task */
+
+               for (prev = NULL, curr = rtcb->mhead; curr != NULL && curr != mutex; prev = curr, curr = curr->flink) ;
+
+               DEBUGASSERT(curr == mutex);
+
+               /* Remove the mutex from the list.  prev == NULL means that the mutex
+                * to be removed is at the head of the list.
+                */
+
+               if (prev == NULL) {
+                       rtcb->mhead = mutex->flink;
+               } else {
+                       prev->flink = mutex->flink;
+               }
+
+               mutex->flink = NULL;
+               irqrestore(flags);
+
+               /* Now release the underlying semaphore */
+
+               ret = pthread_givesemaphore(&mutex->sem);
+       }
+
+       return ret;
+}
+
+/****************************************************************************
+ * Name: pthread_disable_cancel() and pthread_enable_cancel()
+ *
+ * Description:
+ *   Temporarily disable cancellation and return old cancel state, which
+ *   can later be restored.  This is useful when a cancellation point
+ *   function is called from within the OS by a non-cancellation point:
+ *   In certain such cases, we need to defer the cancellation to prevent
+ *   bad things from happening.
+ *
+ * Parameters:
+ *   saved cancel flags for pthread_enable_cancel()
+ *
+ * Return Value:
+ *   old cancel flags for pthread_disable_cancel()
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_CANCELLATION_POINTS
+uint16_t pthread_disable_cancel(void)
+{
+       FAR struct pthread_tcb_s *tcb = (FAR struct pthread_tcb_s *)this_task();
+       irqstate_t flags;
+       uint16_t old;
+
+       /* We need perform the following operations from within a critical section
+        * because it can compete with interrupt level activity.
+        */
+
+       flags = irqsave();
+       old = tcb->cmn.flags & (TCB_FLAG_CANCEL_PENDING | TCB_FLAG_NONCANCELABLE);
+       tcb->cmn.flags &= ~(TCB_FLAG_CANCEL_PENDING | TCB_FLAG_NONCANCELABLE);
+       irqrestore(flags);
+       return old;
+}
+
+void pthread_enable_cancel(uint16_t cancelflags)
+{
+       FAR struct pthread_tcb_s *tcb = (FAR struct pthread_tcb_s *)this_task();
+       irqstate_t flags;
+
+       /* We need perform the following operations from within a critical section
+        * because it can compete with interrupt level activity.
+        */
+
+       flags = irqsave();
+       tcb->cmn.flags |= cancelflags;
+
+       /* What should we do if there is a pending cancellation?
+        *
+        * If the thread is executing with deferred cancellation, we need do
+        * nothing more; the cancellation cannot occur until the next
+        * cancellation point.
+        *
+        * However, if the thread is executing in asynchronous cancellation mode,
+        * then we need to terminate now by simply calling pthread_exit().
+        */
+
+       if ((tcb->cmn.flags & TCB_FLAG_CANCEL_DEFERRED) == 0 && (tcb->cmn.flags & TCB_FLAG_CANCEL_PENDING) != 0) {
+               pthread_exit(NULL);
+       }
+
+       irqrestore(flags);
+}
+#endif                                                 /* CONFIG_CANCELLATION_POINTS */
index 7803508..d233cbd 100644 (file)
@@ -137,7 +137,7 @@ int pthread_mutex_consistent(FAR pthread_mutex_t *mutex)
               /* The thread associated with the PID no longer exists */
 
               mutex->pid    = -1;
-              mutex->flags &= _PTHREAD_MFLAGS_ROBUST;
+               mutex->flags = 0;
 #ifdef CONFIG_MUTEX_TYPES
               mutex->nlocks = 0;
 #endif
diff --git a/os/kernel/pthread/pthread_mutexinconsistent.c b/os/kernel/pthread/pthread_mutexinconsistent.c
new file mode 100644 (file)
index 0000000..98d263e
--- /dev/null
@@ -0,0 +1,118 @@
+/****************************************************************************
+ *
+ * Copyright 2016 Samsung Electronics All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * kernel/pthread/pthread_mutexinconsistent.c
+ *
+ *   Copyright (C) 2007-2009 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 <tinyara/config.h>
+
+#include <pthread.h>
+#include <sched.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <tinyara/sched.h>
+#include <tinyara/semaphore.h>
+
+#include "pthread/pthread.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pthread_mutex_inconsistent
+ *
+ * Description:
+ *   This function is called when a pthread is terminated via either
+ *   pthread_exit() or pthread_cancel().  It will check for any mutexes
+ *   held by exitting thread.  It will mark them as inconsistent and
+ *   then wake up the highest priority waiter for the mutex.  That
+ *   instance of pthread_mutex_lock() will then return EOWNERDEAD.
+ *
+ * Input Parameters:
+ *   tcb -- a reference to the TCB of the exitting pthread.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void pthread_mutex_inconsistent(FAR struct pthread_tcb_s *tcb)
+{
+       FAR struct pthread_mutex_s *mutex;
+       irqstate_t flags;
+
+       DEBUGASSERT(tcb != NULL);
+
+       sched_lock();
+
+       /* Remove and process each mutex from the list of mutexes held by this task */
+
+       while (tcb->mhead != NULL) {
+               /* Remove the mutex from the TCB list */
+
+               flags = irqsave();
+               mutex = tcb->mhead;
+               tcb->mhead = mutex->flink;
+               mutex->flink = NULL;
+               irqrestore(flags);
+
+               /* Mark the mutex as INCONSISTENT and wake up any waiting thread */
+
+               mutex->flags |= _PTHREAD_MFLAGS_INCONSISTENT;
+               (void)pthread_givesemaphore(&mutex->sem);
+       }
+
+       sched_unlock();
+}
index 28e9142..821b91c 100644 (file)
@@ -155,9 +155,16 @@ int pthread_mutex_init(FAR pthread_mutex_t *mutex,
                }
 #endif
 
-               /* Set up attributes unique to the mutex type */
+#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE
+               /* Initial internal fields of the mutex */
+               
+               mutex->flink  = NULL;
+               mutex->flags  = 0;
+#endif
 
 #ifdef CONFIG_MUTEX_TYPES
+               /* Set up attributes unique to the mutex type */
+
                mutex->type = type;
                mutex->nlocks = 0;
 #endif
index fd9970e..547a7e2 100644 (file)
@@ -156,19 +156,24 @@ 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 success */
 
-                               mutex->nlocks++;
-                               ret = OK;
+                               if (mutex->nlocks < INT16_MAX) {
+                                       mutex->nlocks++;
+                                       ret = OK;
+                               } else {
+                                       ret = EOVERFLOW;
+                               }
                        } else
-#endif
                        {
                                /* No, then we would deadlock... return an error (default behavior
                                 * is like PTHREAD_MUTEX_ERRORCHECK)
@@ -177,16 +182,20 @@ int pthread_mutex_lock(FAR pthread_mutex_t *mutex)
                                sdbg("Returning EDEADLK\n");
                                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);
 
                        /* A thread holds the mutex, but there is no such thread.  POSIX
                         * requires that the 'robust' mutex return EOWNERDEAD in this case.
@@ -194,11 +203,17 @@ int pthread_mutex_lock(FAR pthread_mutex_t *mutex)
                         * fo fix the mutex.
                         */
 
+                       mutex->flags |= _PTHREAD_MFLAGS_INCONSISTENT;
                        ret = EOWNERDEAD;
                } else {
-                       /* Take the underlying semaphore, waiting if necessary */
+#endif /* !CONFIG_PTHREAD_MUTEX_UNSAFE */
 
-                       ret = pthread_takesemaphore((FAR sem_t *)&mutex->sem, true);
+                       /* 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);
 
                        /* If we succussfully obtained the semaphore, then indicate
                         * that we own it.
@@ -211,12 +226,6 @@ int pthread_mutex_lock(FAR pthread_mutex_t *mutex)
 #endif
                        }
 
-                       /* Check if we were awakened by a signal.  This might happen if the
-                        * tasking holding the mutex just exitted.
-                        */
-
-                       else if (ret == EINTR) {
-                       }
                }
 
                sched_unlock();
index 538b1f4..b6d59c1 100644 (file)
@@ -155,7 +155,7 @@ int pthread_mutex_trylock(FAR pthread_mutex_t *mutex)
                /* sem_trywait failed */
 
                else {
-                       /* Did it fail because the semaphore was not avaialabl? */
+                       /* Did it fail because the semaphore was not available? */
                        int errcode = get_errno();
                        if (errcode == EAGAIN) {
 #ifdef CONFIG_MUTEX_TYPES
@@ -164,10 +164,16 @@ int pthread_mutex_trylock(FAR pthread_mutex_t *mutex)
                                if (mutex->type == PTHREAD_MUTEX_RECURSIVE && mutex->pid == mypid) {
                                        /* Increment the number of locks held and return successfully. */
 
-                                       mutex->nlocks++;
-                                       ret = OK;
+                                       if (mutex->nlocks < INT16_MAX) {
+                                               mutex->nlocks++;
+                                               ret = OK;
+                                       } else {
+                                               ret = EOVERFLOW;
+                                       }
                                } 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
@@ -176,6 +182,7 @@ int pthread_mutex_trylock(FAR pthread_mutex_t *mutex)
 
                                        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);
 
                                                /* A thread holds the mutex, but there is no such thread.
                                                 * POSIX requires that the 'robust' mutex return EOWNERDEAD
@@ -183,12 +190,15 @@ int pthread_mutex_trylock(FAR pthread_mutex_t *mutex)
                                                 * call pthread_mutx_consistent() fo fix the mutex.
                                                 */
 
+                                               mutex->flags |= _PTHREAD_MFLAGS_INCONSISTENT;
                                                ret = EOWNERDEAD;
                                        }
 
                                /* The mutex is locked by another, active thread */
 
-                                       else {
+                                       else 
+#endif /* CONFIG_PTHREAD_MUTEX_UNSAFE */
+                                       {
                                                ret = EBUSY;
                                        }
 
index 8cfb888..f61255b 100644 (file)
@@ -129,19 +129,32 @@ int pthread_mutex_unlock(FAR pthread_mutex_t *mutex)
 
                sched_lock();
 
-               /* Does the calling thread own the semaphore? */
 
-               if (mutex->pid != (int)getpid()) {
+#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 error (default behavior is like PTHREAD_MUTEX_ERRORCHECK) */
 
                        sdbg("Holder=%d returning EPERM\n", mutex->pid);
                        ret = EPERM;
-               }
+               } else
 
-               /* Yes, the caller owns the semaphore.. Is this a recursive mutex? */
 
 #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
                         * success.
@@ -149,20 +162,28 @@ 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 */
 
                        mutex->pid = -1;
 #ifdef CONFIG_MUTEX_TYPES
                        mutex->nlocks = 0;
 #endif
-                       ret = pthread_givesemaphore((sem_t *)&mutex->sem);
+                       ret = pthread_mutex_give(mutex);
                }
                sched_unlock();
        }
index 9f3336a..ab5fb11 100644 (file)
 #define MAX_TASKS_MASK      (CONFIG_MAX_TASKS-1)
 #define PIDHASH(pid)        ((pid) & MAX_TASKS_MASK)
 
+/* These are macros to access the current CPU and the current task on a CPU.
+ * These macros are intended to support a future SMP implementation.
+ */
+#define current_task(cpu)      ((FAR struct tcb_s *)g_readytorun.head)
+#define this_cpu()             (0)
+#define this_task()            (current_task(this_cpu()))
+
+
 /****************************************************************************
  * Public Type Definitions
  ****************************************************************************/