sched/pthread_cond[timed]wait.c: return special mutex state.

A mutex may be configured with rather exotic options such as recursive, unsafe, etc.  The availability of these mutex options is controlled by configuation settings.  When each option is enabled, additional fields are managed inside of the mutex structure.

pthread_cond_wait() and pthread_timed_wait() do the following atomically:  (1) unlock the mutex, (2) wait for the condition, and (3) restore the mutex lock.  When that lock is restored, pthread_cond_[timed]wait() must also restore the exact configuration of the mutex data structure if these "exotic" features are enabled.
This commit is contained in:
Gregory Nutt 2020-04-05 16:33:44 -06:00 committed by Abdelatif Guettouche
parent 64b398f4c6
commit a48c784cc4
2 changed files with 79 additions and 51 deletions

View File

@ -1,35 +1,20 @@
/**************************************************************************** /****************************************************************************
* sched/pthread/pthread_condtimedwait.c * sched/pthread/pthread_condtimedwait.c
* *
* Copyright (C) 2007-2009, 2013-2017 Gregory Nutt. All rights reserved. * Licensed to the Apache Software Foundation (ASF) under one or more
* Author: Gregory Nutt <gnutt@nuttx.org> * contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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
* *
* Redistribution and use in source and binary forms, with or without * http://www.apache.org/licenses/LICENSE-2.0
* modification, are permitted provided that the following conditions
* are met:
* *
* 1. Redistributions of source code must retain the above copyright * Unless required by applicable law or agreed to in writing, software
* notice, this list of conditions and the following disclaimer. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* 2. Redistributions in binary form must reproduce the above copyright * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* notice, this list of conditions and the following disclaimer in * License for the specific language governing permissions and limitations
* the documentation and/or other materials provided with the * under the License.
* 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.
* *
****************************************************************************/ ****************************************************************************/
@ -164,7 +149,8 @@ static void pthread_condtimedout(int argc, uint32_t pid, uint32_t signo)
* *
****************************************************************************/ ****************************************************************************/
int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex, int pthread_cond_timedwait(FAR pthread_cond_t *cond,
FAR pthread_mutex_t *mutex,
FAR const struct timespec *abstime) FAR const struct timespec *abstime)
{ {
FAR struct tcb_s *rtcb = this_task(); FAR struct tcb_s *rtcb = this_task();
@ -227,8 +213,9 @@ int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex,
sched_lock(); sched_lock();
flags = enter_critical_section(); flags = enter_critical_section();
/* Convert the timespec to clock ticks. We must disable pre-emption /* Convert the timespec to clock ticks. We must disable pre-
* here so that this time stays valid until the wait begins. * emption here so that this time stays valid until the wait
* begins.
*/ */
ret = clock_abstime2ticks(CLOCK_REALTIME, abstime, &ticks); ret = clock_abstime2ticks(CLOCK_REALTIME, abstime, &ticks);
@ -242,15 +229,15 @@ int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex,
} }
else else
{ {
/* Check the absolute time to wait. If it is now or in the past, then /* Check the absolute time to wait. If it is now or in the
* just return with the timedout condition. * past, then just return with the timedout condition.
*/ */
if (ticks <= 0) if (ticks <= 0)
{ {
/* Restore interrupts and indicate that we have already timed out. /* Restore interrupts and indicate that we have already
* (pre-emption will be enabled when we fall through the * timed out. (pre-emption will be enabled when we fall
* if/then/else * through the if/then/else
*/ */
leave_critical_section(flags); leave_critical_section(flags);
@ -258,14 +245,28 @@ int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex,
} }
else else
{ {
#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE
uint8_t mflags;
#endif
#ifdef CONFIG_PTHREAD_MUTEX_TYPES
uint8_t type;
int16_t nlocks;
#endif
/* Give up the mutex */ /* Give up the mutex */
mutex->pid = -1; mutex->pid = -1;
ret = pthread_mutex_give(mutex); #ifndef CONFIG_PTHREAD_MUTEX_UNSAFE
mflags = mutex->flags;
#endif
#ifdef CONFIG_PTHREAD_MUTEX_TYPES
type = mutex->type;
nlocks = mutex->nlocks;
#endif
ret = pthread_mutex_give(mutex);
if (ret != 0) if (ret != 0)
{ {
/* Restore interrupts (pre-emption will be enabled when /* Restore interrupts (pre-emption will be enabled
* we fall through the if/then/else) * when we fall through the if/then/else)
*/ */
leave_critical_section(flags); leave_critical_section(flags);
@ -279,10 +280,10 @@ int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex,
2, (uint32_t)mypid, 2, (uint32_t)mypid,
(uint32_t)SIGCONDTIMEDOUT); (uint32_t)SIGCONDTIMEDOUT);
/* Take the condition semaphore. Do not restore interrupts /* Take the condition semaphore. Do not restore
* until we return from the wait. This is necessary to * interrupts until we return from the wait. This is
* make sure that the watchdog timer and the condition wait * necessary to make sure that the watchdog timer and
* are started atomically. * the condition wait are started atomically.
*/ */
status = nxsem_wait((FAR sem_t *)&cond->sem); status = nxsem_wait((FAR sem_t *)&cond->sem);
@ -293,7 +294,8 @@ int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex,
{ {
/* NO.. Handle the special case where the semaphore /* NO.. Handle the special case where the semaphore
* wait was awakened by the receipt of a signal -- * wait was awakened by the receipt of a signal --
* presumably the signal posted by pthread_condtimedout(). * presumably the signal posted by
* pthread_condtimedout().
*/ */
if (status == -EINTR) if (status == -EINTR)
@ -307,10 +309,10 @@ int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex,
} }
} }
/* The interrupts stay disabled until after we sample the /* The interrupts stay disabled until after we sample
* errno. This is because when debug is enabled and the * the errno. This is because when debug is enabled
* console is used for debug output, then the errno can be * and the console is used for debug output, then the
* altered by interrupt handling! (bad) * errno can be altered by interrupt handling! (bad)
*/ */
leave_critical_section(flags); leave_critical_section(flags);
@ -323,11 +325,18 @@ int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex,
status = pthread_mutex_take(mutex, NULL, false); status = pthread_mutex_take(mutex, NULL, false);
if (status == OK) if (status == OK)
{ {
mutex->pid = mypid; mutex->pid = mypid;
#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE
mutex->flags = mflags;
#endif
#ifdef CONFIG_PTHREAD_MUTEX_TYPES
mutex->type = type;
mutex->nlocks = nlocks;
#endif
} }
else if (ret == 0) else if (ret == 0)
{ {
ret = status; ret = status;
} }
} }

View File

@ -80,12 +80,27 @@ int pthread_cond_wait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex)
} }
else else
{ {
#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE
uint8_t mflags;
#endif
#ifdef CONFIG_PTHREAD_MUTEX_TYPES
uint8_t type;
int16_t nlocks;
#endif
/* Give up the mutex */ /* Give up the mutex */
sinfo("Give up mutex / take cond\n"); sinfo("Give up mutex / take cond\n");
sched_lock(); sched_lock();
mutex->pid = -1; mutex->pid = -1;
#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE
mflags = mutex->flags;
#endif
#ifdef CONFIG_PTHREAD_MUTEX_TYPES
type = mutex->type;
nlocks = mutex->nlocks;
#endif
ret = pthread_mutex_give(mutex); ret = pthread_mutex_give(mutex);
/* Take the semaphore. This may be awakened only be a signal (EINTR) /* Take the semaphore. This may be awakened only be a signal (EINTR)
@ -125,9 +140,13 @@ int pthread_cond_wait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex)
{ {
/* Yes.. Then initialize it properly */ /* Yes.. Then initialize it properly */
mutex->pid = getpid(); mutex->pid = getpid();
#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE
mutex->flags = mflags;
#endif
#ifdef CONFIG_PTHREAD_MUTEX_TYPES #ifdef CONFIG_PTHREAD_MUTEX_TYPES
mutex->nlocks = 1; mutex->type = type;
mutex->nlocks = nlocks;
#endif #endif
} }
} }