mirror of
https://github.com/apache/nuttx.git
synced 2026-06-07 17:33:08 +08:00
task/pthread_cancelpt: Move cancel point handling to libc, data to TLS
This moves task / thread cancel point logic from the NuttX kernel into libc, while the data needed by the cancel point logic is moved to TLS. The change is an enabler to move user-space APIs to libc as well, for a coherent user/kernel separation.
This commit is contained in:
@@ -26,11 +26,11 @@ set(SRCS
|
||||
clock_timespec_add.c
|
||||
clock_timespec_subtract.c
|
||||
clock_getcpuclockid.c
|
||||
clock_getres.c)
|
||||
|
||||
if(NOT CONFIG_CANCELLATION_POINTS)
|
||||
list(APPEND SRCS task_setcanceltype.c task_testcancel.c)
|
||||
endif()
|
||||
clock_getres.c
|
||||
task_cancelpt.c
|
||||
task_setcancelstate.c
|
||||
task_setcanceltype.c
|
||||
task_testcancel.c)
|
||||
|
||||
if(CONFIG_SMP)
|
||||
list(APPEND SRCS sched_cpucount.c)
|
||||
|
||||
@@ -24,10 +24,8 @@ CSRCS += sched_getprioritymax.c sched_getprioritymin.c
|
||||
CSRCS += clock_ticks2time.c clock_time2ticks.c
|
||||
CSRCS += clock_timespec_add.c clock_timespec_subtract.c
|
||||
CSRCS += clock_getcpuclockid.c clock_getres.c
|
||||
|
||||
ifneq ($(CONFIG_CANCELLATION_POINTS),y)
|
||||
CSRCS += task_setcanceltype.c task_testcancel.c
|
||||
endif
|
||||
CSRCS += task_cancelpt.c task_setcancelstate.c task_setcanceltype.c
|
||||
CSRCS += task_testcancel.c
|
||||
|
||||
ifeq ($(CONFIG_SMP),y)
|
||||
CSRCS += sched_cpucount.c
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
/****************************************************************************
|
||||
* libs/libc/sched/task_cancelpt.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Cancellation Points.
|
||||
*
|
||||
* Cancellation points shall occur when a thread is executing the following
|
||||
* functions:
|
||||
*
|
||||
* accept() mq_timedsend() putpmsg() sigtimedwait()
|
||||
* aio_suspend() msgrcv() pwrite() sigwait()
|
||||
* clock_nanosleep() msgsnd() read() sigwaitinfo()
|
||||
* close() msync() readv() sleep()
|
||||
* connect() nanosleep() recv() system()
|
||||
* creat() open() recvfrom() tcdrain()
|
||||
* fcntl() pause() recvmsg() usleep()
|
||||
* fdatasync() poll() select() wait()
|
||||
* fsync() pread() sem_timedwait() waitid()
|
||||
* getmsg() pselect() sem_wait() waitpid()
|
||||
* getpmsg() pthread_cond_timedwait() send() write()
|
||||
* lockf() pthread_cond_wait() sendmsg() writev()
|
||||
* mq_receive() pthread_join() sendto()
|
||||
* mq_send() pthread_testcancel() sigpause()
|
||||
* mq_timedreceive() putmsg() sigsuspend()
|
||||
*
|
||||
* Each of the above function must call enter_cancellation_point() on entry
|
||||
* in order to establish the cancellation point and
|
||||
* leave_cancellation_point() on exit. These functions are described below.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sched.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <nuttx/cancelpt.h>
|
||||
#include <nuttx/tls.h>
|
||||
|
||||
#ifdef CONFIG_CANCELLATION_POINTS
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: enter_cancellation_point
|
||||
*
|
||||
* Description:
|
||||
* Called at the beginning of the cancellation point to establish the
|
||||
* cancellation point. This function does the following:
|
||||
*
|
||||
* 1. If deferred cancellation does not apply to this thread, nothing is
|
||||
* done, otherwise, it
|
||||
* 2. Sets state information in the caller's TCB and increments a 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 thread.
|
||||
*
|
||||
* Input Parameters:
|
||||
* None
|
||||
*
|
||||
* Returned Value:
|
||||
* true is returned if a cancellation is pending but cannot be performed
|
||||
* now due to the nesting level.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
bool enter_cancellation_point(void)
|
||||
{
|
||||
FAR struct tls_info_s *tls = tls_get_info();
|
||||
bool ret = false;
|
||||
|
||||
/* 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 (((tls->tl_cpstate & CANCEL_FLAG_NONCANCELABLE) == 0 &&
|
||||
(tls->tl_cpstate & CANCEL_FLAG_CANCEL_ASYNC) == 0) ||
|
||||
tls->tl_cpcount > 0)
|
||||
{
|
||||
/* Check if there is a pending cancellation */
|
||||
|
||||
if ((tls->tl_cpstate & CANCEL_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 (tls->tl_cpcount == 0)
|
||||
{
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
#else
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, indicate that we are at a cancellation point by
|
||||
* incrementing the nesting level of the cancellation point
|
||||
* functions.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(tls->tl_cpcount < INT16_MAX);
|
||||
tls->tl_cpcount++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: leave_cancellation_point
|
||||
*
|
||||
* Description:
|
||||
* Called at the end of the cancellation point. This function does the
|
||||
* following:
|
||||
*
|
||||
* 1. If deferred cancellation does not apply to this thread, nothing is
|
||||
* done, otherwise, it
|
||||
* 2. Clears state information in the caller's TCB and decrements a
|
||||
* 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 thread.
|
||||
*
|
||||
* Input Parameters:
|
||||
* None
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void leave_cancellation_point(void)
|
||||
{
|
||||
FAR struct tls_info_s *tls = tls_get_info();
|
||||
|
||||
/* 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 (tls->tl_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 (tls->tl_cpcount == 1)
|
||||
{
|
||||
/* We are no longer at the cancellation point */
|
||||
|
||||
tls->tl_cpcount = 0;
|
||||
|
||||
/* If there is a pending cancellation then just exit according to
|
||||
* the type of the thread.
|
||||
*/
|
||||
|
||||
if ((tls->tl_cpstate & CANCEL_FLAG_CANCEL_PENDING) != 0)
|
||||
{
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
#else
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We are not at the outermost nesting level. Just decrment the
|
||||
* nesting level count.
|
||||
*/
|
||||
|
||||
tls->tl_cpcount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: check_cancellation_point
|
||||
*
|
||||
* Description:
|
||||
* Returns true if:
|
||||
*
|
||||
* 1. Deferred cancellation does applies to this thread,
|
||||
* 2. We are within a cancellation point (i.e., the nesting level in the
|
||||
* TCB is greater than zero).
|
||||
*
|
||||
* Input Parameters:
|
||||
* None
|
||||
*
|
||||
* Returned Value:
|
||||
* true is returned if a cancellation is pending but cannot be performed
|
||||
* now due to the nesting level.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
bool check_cancellation_point(void)
|
||||
{
|
||||
FAR struct tls_info_s *tls = tls_get_info();
|
||||
bool ret = false;
|
||||
|
||||
/* If cancellation is disabled on this thread or if this thread is using
|
||||
* asynchronous cancellation, then return false.
|
||||
*
|
||||
* If the cpcount count is greater than zero, then we within a
|
||||
* cancellation and will true if there is a pending cancellation.
|
||||
*/
|
||||
|
||||
if (((tls->tl_cpstate & CANCEL_FLAG_NONCANCELABLE) == 0 &&
|
||||
(tls->tl_cpstate & CANCEL_FLAG_CANCEL_ASYNC) == 0) ||
|
||||
tls->tl_cpcount > 0)
|
||||
{
|
||||
/* Check if there is a pending cancellation. If so, return true. */
|
||||
|
||||
ret = ((tls->tl_cpstate & CANCEL_FLAG_CANCEL_PENDING) != 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_CANCELLATION_POINTS */
|
||||
@@ -0,0 +1,127 @@
|
||||
/****************************************************************************
|
||||
* libs/libc/sched/task_setcancelstate.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sched.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <nuttx/cancelpt.h>
|
||||
#include <nuttx/tls.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: task_setcancelstate
|
||||
*
|
||||
* Description:
|
||||
* The task_setcancelstate() function atomically both sets the calling
|
||||
* task's cancellability state to the indicated state and returns the
|
||||
* previous cancellability state at the location referenced by oldstate.
|
||||
* Legal values for state are TASK_CANCEL_ENABLE and TASK_CANCEL_DISABLE.
|
||||
*
|
||||
* The cancellability state and type of any newly created tasks are
|
||||
* TASK_CANCEL_ENABLE and TASK_CANCEL_DEFERRED respectively.
|
||||
*
|
||||
* Input Parameters:
|
||||
* state - the new cancellability state, either TASK_CANCEL_ENABLE or
|
||||
* TASK_CANCEL_DISABLE
|
||||
* oldstate - The location to return the old cancellability state.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) on success; ERROR is returned on any failure with the
|
||||
* errno value set appropriately.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int task_setcancelstate(int state, FAR int *oldstate)
|
||||
{
|
||||
FAR struct tls_info_s *tls = tls_get_info();
|
||||
int ret = OK;
|
||||
|
||||
/* Return the current state if so requested */
|
||||
|
||||
if (oldstate != NULL)
|
||||
{
|
||||
if ((tls->tl_cpstate & CANCEL_FLAG_NONCANCELABLE) != 0)
|
||||
{
|
||||
*oldstate = TASK_CANCEL_DISABLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
*oldstate = TASK_CANCEL_ENABLE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the new cancellation state */
|
||||
|
||||
if (state == TASK_CANCEL_ENABLE)
|
||||
{
|
||||
/* Clear the non-cancelable flag */
|
||||
|
||||
tls->tl_cpstate &= ~CANCEL_FLAG_NONCANCELABLE;
|
||||
|
||||
/* Check if a cancellation was pending */
|
||||
|
||||
if ((tls->tl_cpstate & CANCEL_FLAG_CANCEL_PENDING) != 0)
|
||||
{
|
||||
#ifdef CONFIG_CANCELLATION_POINTS
|
||||
/* If we are using deferred cancellation? */
|
||||
|
||||
if ((tls->tl_cpstate & CANCEL_FLAG_CANCEL_ASYNC) != 0)
|
||||
#endif
|
||||
{
|
||||
/* No.. We are using asynchronous cancellation. If the
|
||||
* cancellation was pending in this case, then just exit.
|
||||
*/
|
||||
|
||||
tls->tl_cpstate &= ~CANCEL_FLAG_CANCEL_PENDING;
|
||||
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
#else
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (state == TASK_CANCEL_DISABLE)
|
||||
{
|
||||
/* Set the non-cancelable state */
|
||||
|
||||
tls->tl_cpstate |= CANCEL_FLAG_NONCANCELABLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_errno(EINVAL);
|
||||
ret = ERROR;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -22,8 +22,16 @@
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sched.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <nuttx/cancelpt.h>
|
||||
#include <nuttx/tls.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
@@ -46,14 +54,63 @@
|
||||
|
||||
int task_setcanceltype(int type, FAR int *oldtype)
|
||||
{
|
||||
FAR struct tls_info_s *tls = tls_get_info();
|
||||
int ret = OK;
|
||||
|
||||
/* Return the current type if so requested */
|
||||
|
||||
if (oldtype != NULL)
|
||||
{
|
||||
*oldtype = TASK_CANCEL_ASYNCHRONOUS;
|
||||
if ((tls->tl_cpstate & CANCEL_FLAG_CANCEL_ASYNC) != 0)
|
||||
{
|
||||
*oldtype = TASK_CANCEL_ASYNCHRONOUS;
|
||||
}
|
||||
else
|
||||
{
|
||||
*oldtype = TASK_CANCEL_DEFERRED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the requested cancellation type */
|
||||
/* Set the new cancellation type */
|
||||
|
||||
return (type == TASK_CANCEL_ASYNCHRONOUS) ? OK : EINVAL;
|
||||
if (type == TASK_CANCEL_ASYNCHRONOUS)
|
||||
{
|
||||
/* Set the asynchronous cancellation bit */
|
||||
|
||||
tls->tl_cpstate |= CANCEL_FLAG_CANCEL_ASYNC;
|
||||
|
||||
#ifdef CONFIG_CANCELLATION_POINTS
|
||||
/* If we just switched from deferred to asynchronous type and if a
|
||||
* cancellation is pending, then exit now.
|
||||
*/
|
||||
|
||||
if ((tls->tl_cpstate & CANCEL_FLAG_CANCEL_PENDING) != 0 &&
|
||||
(tls->tl_cpstate & CANCEL_FLAG_NONCANCELABLE) == 0)
|
||||
{
|
||||
tls->tl_cpstate &= ~CANCEL_FLAG_CANCEL_PENDING;
|
||||
|
||||
/* Exit according to the type of the thread */
|
||||
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
#else
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef CONFIG_CANCELLATION_POINTS
|
||||
else if (type == TASK_CANCEL_DEFERRED)
|
||||
{
|
||||
/* Set the deferred cancellation type */
|
||||
|
||||
tls->tl_cpstate &= ~CANCEL_FLAG_CANCEL_ASYNC;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
ret = EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,12 @@
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nuttx/cancelpt.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
@@ -40,4 +45,8 @@
|
||||
|
||||
void task_testcancel(void)
|
||||
{
|
||||
#ifdef CONFIG_CANCELLATION_POINTS
|
||||
enter_cancellation_point();
|
||||
leave_cancellation_point();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: tls_get_info
|
||||
* Name: tls_get_info_pid
|
||||
*
|
||||
* Description:
|
||||
* Return a reference to the tls_info_s structure. This is used as part
|
||||
@@ -44,7 +44,7 @@
|
||||
* where CONFIG_TLS_ALIGNED is *not* defined or __KERNEL__ is defined.
|
||||
*
|
||||
* Input Parameters:
|
||||
* None
|
||||
* pid - Thread ID to query, set to 0 to query own
|
||||
*
|
||||
* Returned Value:
|
||||
* A reference to the thread-specific tls_info_s structure is return on
|
||||
@@ -52,13 +52,13 @@
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR struct tls_info_s *tls_get_info(void)
|
||||
FAR struct tls_info_s *tls_get_info_pid(pid_t pid)
|
||||
{
|
||||
FAR struct tls_info_s *info = NULL;
|
||||
struct stackinfo_s stackinfo;
|
||||
int ret;
|
||||
|
||||
ret = nxsched_get_stackinfo(0, &stackinfo);
|
||||
ret = nxsched_get_stackinfo(pid, &stackinfo);
|
||||
if (ret >= 0)
|
||||
{
|
||||
/* The TLS data lies at the lowest address of the stack allocation.
|
||||
|
||||
Reference in New Issue
Block a user