Flesh basic cancellation point support

This commit is contained in:
Gregory Nutt 2016-12-09 09:44:23 -06:00
parent 82a79b9c1b
commit d35e589d56
5 changed files with 186 additions and 23 deletions

View File

@ -168,14 +168,18 @@ EXTERN const pthread_attr_t g_default_pthread_attr;
* count.
* 3. If this is the outermost nesting level, it checks if there is a
* pending cancellation and, if so, calls either exit() or
* pthread_exit(), depending upon the type of the
* pthread_exit(), depending upon the type of the thread.
*
****************************************************************************/
void leave_cancellation_point(void);
#ifdef CONFIG_CANCELLATION_POINTS
void enter_cancellation_point(void);
#else
# define enter_cancellation_point()
#endif
/****************************************************************************
* Name: enter_cancellation_point
* Name: leave_cancellation_point
*
* Description:
* Called at the end of the cancellation point. This function does the
@ -187,11 +191,15 @@ void leave_cancellation_point(void);
* nesting count.
* 3. If this is the outermost nesting level, it checks if there is a
* pending cancellation and, if so, calls either exit() or
* pthread_exit(), depending upon the type of the
* pthread_exit(), depending upon the type of the thread.
*
****************************************************************************/
void enter_cancellation_point(void);
#ifdef CONFIG_CANCELLATION_POINTS
void leave_cancellation_point(void);
#else
# define leave_cancellation_point()
#endif
#undef EXTERN
#ifdef __cplusplus

View File

@ -142,17 +142,16 @@
# define TCB_FLAG_TTYPE_KERNEL (2 << TCB_FLAG_TTYPE_SHIFT) /* Kernel thread */
#define TCB_FLAG_NONCANCELABLE (1 << 2) /* Bit 2: Pthread is non-cancelable */
#define TCB_FLAG_CANCEL_DEFERRED (1 << 3) /* Bit 3: Deferred (vs asynch) cancellation type */
#define TCB_FLAG_CANCEL_POINT (1 << 4) /* Bit 4: At a cancellation point */
#define TCB_FLAG_CANCEL_PENDING (1 << 5) /* Bit 5: Pthread cancel is pending */
#define TCB_FLAG_POLICY_SHIFT (6) /* Bit 6-7: Scheduling policy */
#define TCB_FLAG_CANCEL_PENDING (1 << 4) /* Bit 4: Pthread cancel is pending */
#define TCB_FLAG_POLICY_SHIFT (5) /* Bit 5-6: Scheduling policy */
#define TCB_FLAG_POLICY_MASK (3 << TCB_FLAG_POLICY_SHIFT)
# define TCB_FLAG_SCHED_FIFO (0 << TCB_FLAG_POLICY_SHIFT) /* FIFO scheding policy */
# define TCB_FLAG_SCHED_RR (1 << TCB_FLAG_POLICY_SHIFT) /* Round robin scheding policy */
# define TCB_FLAG_SCHED_SPORADIC (2 << TCB_FLAG_POLICY_SHIFT) /* Sporadic scheding policy */
# define TCB_FLAG_SCHED_OTHER (3 << TCB_FLAG_POLICY_SHIFT) /* Other scheding policy */
#define TCB_FLAG_CPU_LOCKED (1 << 8) /* Bit 8: Locked to this CPU */
#define TCB_FLAG_EXIT_PROCESSING (1 << 9) /* Bit 9: Exitting */
/* Bits 10-15: Available */
#define TCB_FLAG_CPU_LOCKED (1 << 7) /* Bit 7: Locked to this CPU */
#define TCB_FLAG_EXIT_PROCESSING (1 << 8) /* Bit 8: Exitting */
/* Bits 9-15: Available */
/* Values for struct task_group tg_flags */
@ -584,6 +583,9 @@ struct tcb_s
#ifdef CONFIG_SMP
int16_t irqcount; /* 0=interrupts enabled */
#endif
#ifdef CONFIG_CANCELLATION_POINTS
int16_t cpcount; /* Nested cancellation point count */
#endif
#if CONFIG_RR_INTERVAL > 0 || defined(CONFIG_SCHED_SPORADIC)
int32_t timeslice; /* RR timeslice OR Sporadic budget */

View File

@ -111,9 +111,9 @@ int pthread_setcancelstate(int state, FAR int *oldstate)
* notify of the cancellation.
*/
if ((tcb->flags & TCB_FLAG_CANCEL_POINT) != 0)
if (tcb->cpcount > 0)
{
notify_cancellation();
notify_cancellation(tcb);
}
}
else

View File

@ -83,7 +83,7 @@ void task_recover(FAR struct tcb_s *tcb);
/* Cancellation points */
#ifdef CONFIG_CANCELLATION_POINTS
void notify_cancellation(void);
void notify_cancellation(FAR struct tcb_s *tcb);
#endif
#endif /* __SCHED_TASK_TASK_H */

View File

@ -68,9 +68,14 @@
#include <nuttx/config.h>
#include <sched.h>
#include <errno.h>
#include <nuttx/irq.h>
#include <nuttx/pthread.h>
#include "sched/sched.h"
#include "semaphore/semaphore.h"
#include "mqueue/mqueue.h"
#include "task/task.h"
#ifdef CONFIG_CANCELLATION_POINTS
@ -92,17 +97,67 @@
* count.
* 3. If this is the outermost nesting level, it checks if there is a
* pending cancellation and, if so, calls either exit() or
* pthread_exit(), depending upon the type of the
* pthread_exit(), depending upon the type of the thread.
*
****************************************************************************/
void leave_cancellation_point(void)
void enter_cancellation_point(void)
{
#warning Missing logic
FAR struct tcb_s *tcb = this_task();
/* Disabling pre-emption should provide sufficient protection. We only
* need the TCB to be stationary (no interrupt level modification is
* anticipated).
*/
sched_lock();
/* If cancellation is disabled on this thread or if this thread is using
* asynchronous cancellation, then do nothing.
*
* Special case: if the cpcount count is greater than zero, then we are
* nested and the above condition was certainly true at the outermost
* nesting level.
*/
if (((tcb->flags & TCB_FLAG_NONCANCELABLE) == 0 &&
(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.
*/
if ((tcb->flags & TCB_FLAG_CANCEL_PENDING) != 0 &&
tcb->cpcount == 0)
{
#ifndef CONFIG_DISABLE_PTHREAD
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD)
{
pthread_exit(NULL);
}
else
#endif
{
exit(EXIT_FAILURE);
}
}
/* Otherwise, indicate that we are at a cancellation point by
* incrementing the nesting level of the cancellation point
* functions.
*/
DEBUGASSERT(tcb->cpcount < INT16_MAX);
tcb->cpcount++;
}
sched_unlock();
}
/****************************************************************************
* Name: enter_cancellation_point
* Name: leave_cancellation_point
*
* Description:
* Called at the end of the cancellation point. This function does the
@ -114,13 +169,68 @@ void leave_cancellation_point(void)
* nesting count.
* 3. If this is the outermost nesting level, it checks if there is a
* pending cancellation and, if so, calls either exit() or
* pthread_exit(), depending upon the type of the
* pthread_exit(), depending upon the type of the thread.
*
****************************************************************************/
void enter_cancellation_point(void)
void leave_cancellation_point(void)
{
#warning Missing logic
FAR struct tcb_s *tcb = this_task();
/* Disabling pre-emption should provide sufficient protection. We only
* need the TCB to be stationary (no interrupt level modification is
* anticipated).
*/
sched_lock();
/* If cancellation is disabled on this thread or if this thread is using
* asynchronous cancellation, then do nothing. Here we check only the
* nesting level: if the cpcount count is greater than zero, then the
* required condition was certainly true at the outermost nesting level.
*/
if (tcb->cpcount > 0)
{
/* Decrement the nesting level. If if would decrement to zero, then
* we are at the outermost nesting level and may need to do more.
*/
if (tcb->cpcount == 1)
{
/* We are no longer at the cancellation point */
tcb->cpcount = 0;
/* If there is a pending cancellation then just exit according to
* the type of the thread.
*/
if ((tcb->flags & TCB_FLAG_CANCEL_PENDING) != 0)
{
#ifndef CONFIG_DISABLE_PTHREAD
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD)
{
pthread_exit(NULL);
}
else
#endif
{
exit(EXIT_FAILURE);
}
}
}
else
{
/* We are not at the outermost nesting level. Just decrment the
* nesting level count.
*/
tcb->cpcount--;
}
}
sched_unlock();
}
/****************************************************************************
@ -136,9 +246,52 @@ void enter_cancellation_point(void)
*
****************************************************************************/
void notify_cancellation(void)
void notify_cancellation(FAR struct tcb_s *tcb)
{
#warning Missing logic
irqstate_t flags;
/* We need perform the following operations from within a critical section
* because it can compete with interrupt level activity.
*/
flags = enter_critical_section();
/* Make sure that the cancellation pending indication is set. */
tcb->flags |= TCB_FLAG_CANCEL_PENDING;
/* We only notify the cancellation if (1) the thread has not disabled
* cancellation, (2) the thread uses the deffered cancellation mode,
* (3) the thread is waiting within a cancellation point.
*/
if (((tcb->flags & TCB_FLAG_NONCANCELABLE) == 0 &&
(tcb->flags & TCB_FLAG_CANCEL_DEFERRED) != 0) ||
tcb->cpcount > 0)
{
/* If the thread is blocked waiting for a semaphore, then the thread
* must be unblocked to handle the cancellation.
*/
if (tcb->task_state == TSTATE_WAIT_SEM)
{
sem_waitirq(tcb, ECANCELED);
}
/* If the thread is blocked waiting on a message queue, then the
* thread must be unblocked to handle the cancellation.
*/
#ifndef CONFIG_DISABLE_MQUEUE
if (tcb->task_state == TSTATE_WAIT_MQNOTEMPTY ||
tcb->task_state == TSTATE_WAIT_MQNOTFULL)
{
mq_waitirq(tcb, ECANCELED);
}
#endif
}
leave_critical_section(flags);
}
#endif /* CONFIG_CANCELLATION_POINTS */