Cancellation points: Close up some logic to eliminte some race conditions.

This commit is contained in:
Gregory Nutt 2016-12-10 08:36:58 -06:00
parent e9d3c3362a
commit bc3ca25cc7
36 changed files with 149 additions and 68 deletions

View File

@ -88,7 +88,7 @@ int close(int fd)
/* close() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
#if CONFIG_NFILE_DESCRIPTORS > 0
/* Did we get a valid file descriptor? */

View File

@ -223,7 +223,7 @@ int fcntl(int fd, int cmd, ...)
/* fcntl() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Setup to access the variable argument list */

View File

@ -122,7 +122,7 @@ int fsync(int fd)
/* fsync() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Get the file structure corresponding to the file descriptor. */

View File

@ -105,7 +105,7 @@ int open(const char *path, int oflags, ...)
/* open() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* If the file is opened for creation, then get the mode bits */

View File

@ -368,7 +368,7 @@ int poll(FAR struct pollfd *fds, nfds_t nfds, int timeout)
/* poll() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* This semaphore is used for signaling and, hence, should not have
* priority inheritance enabled.

View File

@ -145,7 +145,7 @@ ssize_t pread(int fd, FAR void *buf, size_t nbytes, off_t offset)
/* pread() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Get the file structure corresponding to the file descriptor. */

View File

@ -143,7 +143,7 @@ ssize_t pwrite(int fd, FAR const void *buf, size_t nbytes, off_t offset)
/* pread() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Get the file structure corresponding to the file descriptor. */
@ -161,6 +161,6 @@ ssize_t pwrite(int fd, FAR const void *buf, size_t nbytes, off_t offset)
ret = file_pwrite(filep, buf, nbytes, offset);
}
enter_cancellation_point();
(void)enter_cancellation_point();
return ret;
}

View File

@ -138,7 +138,7 @@ ssize_t read(int fd, FAR void *buf, size_t nbytes)
/* read() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Did we get a valid file descriptor? */

View File

@ -113,7 +113,7 @@ int select(int nfds, FAR fd_set *readfds, FAR fd_set *writefds,
/* select() is cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* How many pollfd structures do we need to allocate? */

View File

@ -166,7 +166,7 @@ ssize_t write(int fd, FAR const void *buf, size_t nbytes)
/* write() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Did we get a valid file descriptor? */

View File

@ -42,6 +42,8 @@
****************************************************************************/
#include <nuttx/config.h>
#include <stdbool.h>
#include <pthread.h>
#include <sched.h>
@ -170,12 +172,19 @@ EXTERN const pthread_attr_t g_default_pthread_attr;
* pending cancellation and, if so, calls either exit() or
* pthread_exit(), depending upon the type of the thread.
*
* Input Parameters:
* None
*
* Returned Value
* true is returned if a cancellation is pending but cannot be performed
* now due to the nesting level.
*
****************************************************************************/
#ifdef CONFIG_CANCELLATION_POINTS
void enter_cancellation_point(void);
bool enter_cancellation_point(void);
#else
# define enter_cancellation_point()
# define enter_cancellation_point() false
#endif
/****************************************************************************
@ -193,6 +202,12 @@ void enter_cancellation_point(void);
* pending cancellation and, if so, calls either exit() or
* pthread_exit(), depending upon the type of the thread.
*
* Input Parameters:
* None
*
* Returned Value
* None
*
****************************************************************************/
#ifdef CONFIG_CANCELLATION_POINTS

View File

@ -135,7 +135,7 @@ int psock_accept(FAR struct socket *psock, FAR struct sockaddr *addr,
/* Treat as a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Is the socket a stream? */
@ -365,7 +365,7 @@ int accept(int sockfd, FAR struct sockaddr *addr, FAR socklen_t *addrlen)
/* accept() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Verify that the sockfd corresponds to valid, allocated socket */

View File

@ -519,7 +519,7 @@ int psock_connect(FAR struct socket *psock, FAR const struct sockaddr *addr,
/* Treat as a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Verify that the psock corresponds to valid, allocated socket */
@ -752,7 +752,7 @@ int connect(int sockfd, FAR const struct sockaddr *addr, socklen_t addrlen)
/* accept() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Get the underlying socket structure */

View File

@ -1854,7 +1854,7 @@ ssize_t psock_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len,
/* Treat as a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Verify that non-NULL pointers were passed */
@ -2087,7 +2087,7 @@ ssize_t recvfrom(int sockfd, FAR void *buf, size_t len, int flags,
/* recvfrom() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Get the underlying socket structure */

View File

@ -126,7 +126,7 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len,
/* Treat as a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
switch (psock->s_type)
{
@ -273,7 +273,7 @@ ssize_t send(int sockfd, FAR const void *buf, size_t len, int flags)
/* send() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Get the underlying socket structure */

View File

@ -314,7 +314,7 @@ ssize_t sendto(int sockfd, FAR const void *buf, size_t len, int flags,
/* sendto() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Get the underlying socket structure */

View File

@ -50,6 +50,7 @@
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/pthread.h>
#include "sched/sched.h"
#include "mqueue/mqueue.h"
@ -141,6 +142,21 @@ FAR struct mqueue_msg_s *mq_waitreceive(mqd_t mqdes)
FAR struct mqueue_inode_s *msgq;
FAR struct mqueue_msg_s *rcvmsg;
/* mq_waitreceive() is not a cancellation point, but it is always called
* from a cancellation point.
*/
if (enter_cancellation_point())
{
/* If there is a pending cancellation, then do not perform
* the wait. Exit now with ECANCELED.
*/
set_errno(ECANCELED);
leave_cancellation_point();
return NULL;
}
/* Get a pointer to the message queue */
msgq = mqdes->msgq;
@ -195,6 +211,7 @@ FAR struct mqueue_msg_s *mq_waitreceive(mqd_t mqdes)
msgq->nmsgs--;
}
leave_cancellation_point();
return rcvmsg;
}

View File

@ -106,7 +106,7 @@ ssize_t mq_receive(mqd_t mqdes, FAR char *msg, size_t msglen,
/* mq_receive() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Verify the input parameters and, in case of an error, set
* errno appropriately.

View File

@ -106,7 +106,7 @@ int mq_send(mqd_t mqdes, FAR const char *msg, size_t msglen, int prio)
/* mq_send() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Verify the input parameters -- setting errno appropriately
* on any failures to verify.

View File

@ -1,5 +1,5 @@
/****************************************************************************
* sched/mqueue/mq_send.c
* sched/mqueue/mq_sndinternal.c
*
* Copyright (C) 2007, 2009, 2013-2016 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
@ -53,6 +53,7 @@
#include <nuttx/arch.h>
#include <nuttx/sched.h>
#include <nuttx/signal.h>
#include <nuttx/pthread.h>
#include "sched/sched.h"
#ifndef CONFIG_DISABLE_SIGNALS
@ -232,6 +233,21 @@ int mq_waitsend(mqd_t mqdes)
FAR struct tcb_s *rtcb;
FAR struct mqueue_inode_s *msgq;
/* mq_waitsend() is not a cancellation point, but it is always called from
* a cancellation point.
*/
if (enter_cancellation_point())
{
/* If there is a pending cancellation, then do not perform
* the wait. Exit now with ECANCELED.
*/
set_errno(ECANCELED);
leave_cancellation_point();
return ERROR;
}
/* Get a pointer to the message queue */
msgq = mqdes->msgq;
@ -249,6 +265,7 @@ int mq_waitsend(mqd_t mqdes)
/* No... We will return an error to the caller. */
set_errno(EAGAIN);
leave_cancellation_point();
return ERROR;
}
@ -283,12 +300,14 @@ int mq_waitsend(mqd_t mqdes)
if (get_errno() != OK)
{
leave_cancellation_point();
return ERROR;
}
}
}
}
leave_cancellation_point();
return OK;
}

View File

@ -177,7 +177,7 @@ ssize_t mq_timedreceive(mqd_t mqdes, FAR char *msg, size_t msglen,
/* mq_timedreceive() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Verify the input parameters and, in case of an error, set
* errno appropriately.

View File

@ -181,7 +181,7 @@ int mq_timedsend(mqd_t mqdes, FAR const char *msg, size_t msglen, int prio,
/* mq_timedsend() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Verify the input parameters -- setting errno appropriately
* on any failures to verify.

View File

@ -181,7 +181,7 @@ int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex,
/* pthread_cond_timedwait() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Make sure that non-NULL references were provided. */

View File

@ -77,7 +77,7 @@ int pthread_cond_wait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex)
/* pthread_cond_wait() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Make sure that non-NULL references were provided. */

View File

@ -96,7 +96,7 @@ int pthread_join(pthread_t thread, FAR pthread_addr_t *pexit_value)
/* pthread_join() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* First make sure that this is not an attempt to join to
* ourself.

View File

@ -62,6 +62,6 @@
void pthread_testcancel(void)
{
enter_cancellation_point();
(void)enter_cancellation_point();
leave_cancellation_point();
}

View File

@ -167,7 +167,7 @@ int waitid(idtype_t idtype, id_t id, FAR siginfo_t *info, int options)
/* waitid() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* MISSING LOGIC: If WNOHANG is provided in the options, then this function
* should returned immediately. However, there is no mechanism available now

View File

@ -188,7 +188,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options)
/* waitpid() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* None of the options are supported */
@ -317,7 +317,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options)
/* waitpid() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* None of the options are supported */

View File

@ -106,7 +106,7 @@ int sem_timedwait(FAR sem_t *sem, FAR const struct timespec *abstime)
/* sem_timedwait() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Verify the input parameters and, in case of an error, set
* errno appropriately.

View File

@ -87,21 +87,31 @@ int sem_wait(FAR sem_t *sem)
DEBUGASSERT(sem != NULL && up_interrupt_context() == false);
/* sem_wait is a cancellation point */
/* The following operations must be performed with interrupts
* disabled because sem_post() may be called from an interrupt
* handler.
*/
enter_cancellation_point();
flags = enter_critical_section();
/* sem_wait() is a cancellation point */
if (enter_cancellation_point())
{
/* If there is a pending cancellation, then do not perform
* the wait. Exit now with ECANCELED.
*/
set_errno(ECANCELED);
leave_cancellation_point();
leave_critical_section(flags);
return ERROR;
}
/* Make sure we were supplied with a valid semaphore. */
if (sem != NULL)
{
/* The following operations must be performed with interrupts
* disabled because sem_post() may be called from an interrupt
* handler.
*/
flags = enter_critical_section();
/* Check if the lock is available */
if (sem->semcount > 0)
@ -191,10 +201,6 @@ int sem_wait(FAR sem_t *sem)
sched_unlock();
#endif
}
/* Interrupts may now be enabled. */
leave_critical_section(flags);
}
else
{
@ -202,5 +208,6 @@ int sem_wait(FAR sem_t *sem)
}
leave_cancellation_point();
leave_critical_section(flags);
return ret;
}

View File

@ -115,7 +115,7 @@ int nanosleep(FAR const struct timespec *rqtp, FAR struct timespec *rmtp)
/* nanosleep() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
if (!rqtp || rqtp->tv_nsec < 0 || rqtp->tv_nsec >= 1000000000)
{

View File

@ -80,7 +80,7 @@ int pause(void)
/* pause() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Set up for the sleep. Using the empty set means that we are not
* waiting for any particular signal. However, any unmasked signal

View File

@ -101,7 +101,7 @@ int sigsuspend(FAR const sigset_t *set)
/* sigsuspend() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Several operations must be performed below: We must determine if any
* signal is pending and, if not, wait for the signal. Since signals can

View File

@ -176,7 +176,7 @@ int sigtimedwait(FAR const sigset_t *set, FAR struct siginfo *info,
/* sigtimedwait() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
sched_lock(); /* Not necessary */
/* Several operations must be performed below: We must determine if any

View File

@ -72,7 +72,7 @@ int sigwaitinfo(FAR const sigset_t *set, FAR struct siginfo *info)
/* sigwaitinfo() is a cancellation point */
enter_cancellation_point();
(void)enter_cancellation_point();
/* Just a wrapper around sigtimedwait() */

View File

@ -99,11 +99,19 @@
* pending cancellation and, if so, calls either exit() or
* pthread_exit(), depending upon the type of the thread.
*
* Input Parameters:
* None
*
* Returned Value
* true is returned if a cancellation is pending but cannot be performed
* now due to the nesting level.
*
****************************************************************************/
void enter_cancellation_point(void)
bool enter_cancellation_point(void)
{
FAR struct tcb_s *tcb = this_task();
bool ret = false;
/* Disabling pre-emption should provide sufficient protection. We only
* need the TCB to be stationary (no interrupt level modification is
@ -126,24 +134,32 @@ void enter_cancellation_point(void)
(tcb->flags & TCB_FLAG_CANCEL_DEFERRED) != 0) ||
tcb->cpcount > 0)
{
/* If there is a pending cancellation and we are at the outermost
* nesting level of cancellation function calls, then just exit
* according to the type of the thread.
*/
/* Check if there is a pending cancellation */
if ((tcb->flags & TCB_FLAG_CANCEL_PENDING) != 0 &&
tcb->cpcount == 0)
if ((tcb->flags & TCB_FLAG_CANCEL_PENDING) != 0)
{
/* Yes... return true (if we don't exit here) */
ret = true;
/* If there is a pending cancellation and we are at the outermost
* nesting level of cancellation function calls, then exit
* according to the type of the thread.
*/
if (tcb->cpcount == 0)
{
#ifndef CONFIG_DISABLE_PTHREAD
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD)
{
pthread_exit(PTHREAD_CANCELED);
}
else
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD)
{
pthread_exit(PTHREAD_CANCELED);
}
else
#endif
{
exit(EXIT_FAILURE);
}
{
exit(EXIT_FAILURE);
}
}
}
/* Otherwise, indicate that we are at a cancellation point by
@ -156,6 +172,7 @@ void enter_cancellation_point(void)
}
sched_unlock();
return ret;
}
/****************************************************************************
@ -173,6 +190,12 @@ void enter_cancellation_point(void)
* pending cancellation and, if so, calls either exit() or
* pthread_exit(), depending upon the type of the thread.
*
* Input Parameters:
* None
*
* Returned Value
* None
*
****************************************************************************/
void leave_cancellation_point(void)