From: jc_.kim Date: Thu, 9 Mar 2017 06:03:03 +0000 (+0900) Subject: Merge Posix Cancellation Point X-Git-Tag: 1.1_Public_Release~655^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ae71734ea28978efa4f95929b4f7255d38ea92f0;p=rtos%2Ftinyara.git Merge Posix Cancellation Point Merge Posix Cancellation Point referenced by Nuttx 7.18 --- diff --git a/lib/libc/pthread/Make.defs b/lib/libc/pthread/Make.defs index 6dd6ef3..8bfe2a3 100644 --- a/lib/libc/pthread/Make.defs +++ b/lib/libc/pthread/Make.defs @@ -61,9 +61,11 @@ CSRCS += pthread_attrinit.c pthread_attrdestroy.c \ pthread_barrierattrgetpshared.c pthread_barrierattrsetpshared.c \ pthread_condattrinit.c pthread_condattrdestroy.c \ pthread_mutexattrinit.c pthread_mutexattrdestroy.c \ - pthread_mutexattrgetpshared.c pthread_mutexattrsetpshared.c + pthread_mutexattrgetpshared.c pthread_mutexattrsetpshared.c \ + pthread_setcancelstate.c pthread_setcanceltype.c \ + pthread_testcancel.c -ifeq ($(CONFIG_IOTIVITY_SCONS_BUILD),y) +ifeq ($(CONFIG_ENABLE_IOTIVITY),y) CSRCS += pthread_condattrsetclock.c endif diff --git a/lib/libc/pthread/pthread_setcancelstate.c b/lib/libc/pthread/pthread_setcancelstate.c new file mode 100644 index 0000000..f77e7b3 --- /dev/null +++ b/lib/libc/pthread/pthread_setcancelstate.c @@ -0,0 +1,107 @@ +/**************************************************************************** + * + * 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. + * + ****************************************************************************/ +/**************************************************************************** + * libc/pthread/pthread_setcancelstate.c + * + * Copyright (C) 2007, 2008, 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 +#include +#include +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ +/* These are defined in different header files but must have the same values. */ + +#if PTHREAD_CANCEL_ENABLE != TASK_CANCEL_ENABLE +# error We must have PTHREAD_CANCEL_ENABLE == TASK_CANCEL_ENABLE +#endif + +#if PTHREAD_CANCEL_DISABLE != TASK_CANCEL_DISABLE +# error We must have PTHREAD_CANCEL_DISABLE == TASK_CANCEL_DISABLE +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pthread_setcancelstate + * + * Description: + * The pthread_setcancelstate() function atomically both sets the calling + * thread's cancelability state to the indicated state and returns the + * previous cancelability state at the location referenced by oldstate. + * Legal values for state are PTHREAD_CANCEL_ENABLE and + * PTHREAD_CANCEL_DISABLE. + * + * The cancelability state and type of any newly created threads, + * including the thread in which main() was first invoked, are + * PTHREAD_CANCEL_ENABLE and PTHREAD_CANCEL_DEFERRED respectively. + * + ****************************************************************************/ + +int pthread_setcancelstate(int state, FAR int *oldstate) +{ + int ret; + + /* task_setcancelstate() can do this */ + + ret = task_setcancelstate(state, oldstate); + if (ret < 0) { + ret = errno; + } + + return ret; +} diff --git a/lib/libc/pthread/pthread_setcanceltype.c b/lib/libc/pthread/pthread_setcanceltype.c new file mode 100644 index 0000000..87514d3 --- /dev/null +++ b/lib/libc/pthread/pthread_setcanceltype.c @@ -0,0 +1,108 @@ +/**************************************************************************** + * + * 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. + * + ****************************************************************************/ +/**************************************************************************** + * libc/pthread/pthread_setcanceltype.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* The following are defined in different header files but must have the + * same values. + */ + +#if PTHREAD_CANCEL_DEFERRED != TASK_CANCEL_DEFERRED +# error We must have PTHREAD_CANCEL_DEFERRED == TASK_CANCEL_DEFERRED +#endif + +#if PTHREAD_CANCEL_ASYNCHRONOUS != TASK_CANCEL_ASYNCHRONOUS +# error We must have PTHREAD_CANCEL_ASYNCHRONOUS == TASK_CANCEL_ASYNCHRONOUS +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pthread_setcancelstate + * + * Description: + * The pthread_setcanceltype() function atomically both sets the calling + * thread's cancelability type to the indicated type and returns the + * previous cancelability type at the location referenced by oldtype + * Legal values for type are PTHREAD_CANCEL_DEFERRED and + * PTHREAD_CANCEL_ASYNCHRONOUS. + * + * The cancelability state and type of any newly created threads, + * including the thread in which main() was first invoked, are + * PTHREAD_CANCEL_ENABLE and PTHREAD_CANCEL_DEFERRED respectively. + * + ****************************************************************************/ + +int pthread_setcanceltype(int type, FAR int *oldtype) +{ + int ret; + + /* task_setcanceltype() can do this */ + + ret = task_setcanceltype(type, oldtype); + if (ret < 0) { + ret = errno; + } + + return ret; +} diff --git a/lib/libc/pthread/pthread_testcancel.c b/lib/libc/pthread/pthread_testcancel.c new file mode 100644 index 0000000..bff3543 --- /dev/null +++ b/lib/libc/pthread/pthread_testcancel.c @@ -0,0 +1,79 @@ +/**************************************************************************** + * + * 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. + * + ****************************************************************************/ +/**************************************************************************** + * libc/pthread/pthread_testcancel.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pthread_testcancel + * + * Description: + * The pthread_testcancel() function creates a cancellation point in the + * calling thread. The pthread_testcancel() function has no effect if + * cancelability is disabled + * + ****************************************************************************/ + +void pthread_testcancel(void) +{ + /* task_testcancel() does the real work */ + + task_testcancel(); +} diff --git a/lib/libc/sched/Make.defs b/lib/libc/sched/Make.defs index 0ab5f53..5310aca 100644 --- a/lib/libc/sched/Make.defs +++ b/lib/libc/sched/Make.defs @@ -58,6 +58,10 @@ ifeq ($(CONFIG_BUILD_PROTECTED),y) CSRCS += task_startup.c endif +ifeq ($(CONFIG_CANCELLATION_POINTS),y) +CSRCS += task_setcanceltype.c task_testcancel.c +endif + # Add the sched directory to the build DEPPATH += --dep-path sched diff --git a/lib/libc/sched/task_setcanceltype.c b/lib/libc/sched/task_setcanceltype.c new file mode 100644 index 0000000..aeee9d4 --- /dev/null +++ b/lib/libc/sched/task_setcanceltype.c @@ -0,0 +1,90 @@ +/**************************************************************************** + * + * 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. + * + ****************************************************************************/ +/**************************************************************************** + * libc/sched/task_setcanceltype.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: task_setcancelstate + * + * Description: + * The task_setcanceltype() function atomically both sets the calling + * task's cancelability type to the indicated type and returns the + * previous cancelability type at the location referenced by oldtype + * Legal values for type are TASK_CANCEL_DEFERRED and + * TASK_CANCEL_ASYNCHRONOUS. + * + * The cancelability state and type of any newly created tasks are + * TASK_CANCEL_ENABLE and TASK_CANCEL_DEFERRED respectively. + * + ****************************************************************************/ + +int task_setcanceltype(int type, FAR int *oldtype) +{ + /* Return the current type if so requrested */ + + if (oldtype != NULL) { + *oldtype = TASK_CANCEL_ASYNCHRONOUS; + } + + /* Check the requested cancellation type */ + + return (type == TASK_CANCEL_ASYNCHRONOUS) ? OK : ENOSYS; +} diff --git a/lib/libc/sched/task_testcancel.c b/lib/libc/sched/task_testcancel.c new file mode 100644 index 0000000..db80e64 --- /dev/null +++ b/lib/libc/sched/task_testcancel.c @@ -0,0 +1,75 @@ +/**************************************************************************** + * + * 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. + * + ****************************************************************************/ +/**************************************************************************** + * libc/sched/task_testcancel.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: task_testcancel + * + * Description: + * The task_testcancel() function creates a cancellation point in the + * calling thread. The task_testcancel() function has no effect if + * cancelability is disabled + * + ****************************************************************************/ + +void task_testcancel(void) +{ +} diff --git a/os/fs/vfs/fs_close.c b/os/fs/vfs/fs_close.c index 9bc38db..8b26a23 100644 --- a/os/fs/vfs/fs_close.c +++ b/os/fs/vfs/fs_close.c @@ -60,6 +60,7 @@ #include #include #include +#include #if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0 #include @@ -99,7 +100,11 @@ int close(int fd) int err; #if CONFIG_NFILE_DESCRIPTORS > 0 int ret; +#endif + /* close() is a cancellation point */ + (void)enter_cancellation_point(); +#if CONFIG_NFILE_DESCRIPTORS > 0 /* Did we get a valid file descriptor? */ if ((unsigned int)fd >= CONFIG_NFILE_DESCRIPTORS) @@ -109,7 +114,9 @@ int close(int fd) #if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0 if ((unsigned int)fd < (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS)) { - return net_close(fd); + ret = net_close(fd); + leave_cancellation_point(); + return ret; } else #endif { @@ -135,11 +142,13 @@ int close(int fd) err = -ret; goto errout; } + leave_cancellation_point(); return OK; #endif errout: set_errno(err); + leave_cancellation_point(); return ERROR; } diff --git a/os/fs/vfs/fs_fcntl.c b/os/fs/vfs/fs_fcntl.c index 891c758..54f04f1 100644 --- a/os/fs/vfs/fs_fcntl.c +++ b/os/fs/vfs/fs_fcntl.c @@ -64,6 +64,7 @@ #include #include #include +#include #include "inode/inode.h" @@ -242,6 +243,9 @@ int fcntl(int fd, int cmd, ...) va_list ap; int ret; + /* fcntl() is a cancellation point */ + (void)enter_cancellation_point(); + /* Setup to access the variable argument list */ va_start(ap, cmd); @@ -257,6 +261,7 @@ int fcntl(int fd, int cmd, ...) /* The errno value has already been set */ va_end(ap); + leave_cancellation_point(); return ERROR; } @@ -282,5 +287,6 @@ int fcntl(int fd, int cmd, ...) } va_end(ap); + leave_cancellation_point(); return ret; } diff --git a/os/fs/vfs/fs_fsync.c b/os/fs/vfs/fs_fsync.c index bf8b153..806d506 100644 --- a/os/fs/vfs/fs_fsync.c +++ b/os/fs/vfs/fs_fsync.c @@ -61,8 +61,9 @@ #include #include -#include #include +#include +#include #include "inode/inode.h" @@ -146,6 +147,10 @@ errout: int fsync(int fd) { FAR struct file *filep; + int ret; + + /* fsync() is a cancellation point */ + (void)enter_cancellation_point(); /* Get the file structure corresponding to the file descriptor. */ @@ -153,12 +158,15 @@ int fsync(int fd) if (!filep) { /* The errno value has already been set */ + leave_cancellation_point(); return ERROR; } /* Perform the fsync operation */ - return file_fsync(filep); + ret = file_fsync(filep); + leave_cancellation_point(); + return ret; } #endif /* !CONFIG_DISABLE_MOUNTPOINT */ diff --git a/os/fs/vfs/fs_open.c b/os/fs/vfs/fs_open.c index a712177..4d57255 100644 --- a/os/fs/vfs/fs_open.c +++ b/os/fs/vfs/fs_open.c @@ -65,6 +65,7 @@ #include #endif +#include #include #include "inode/inode.h" @@ -118,6 +119,9 @@ int open(const char *path, int oflags, ...) #warning "File creation not implemented" #endif + /* open() is a cancellation point */ + (void)enter_cancellation_point(); + /* If the file is opened for creation, then get the mode bits */ if ((oflags & (O_WRONLY | O_CREAT)) != 0) { @@ -177,7 +181,7 @@ int open(const char *path, int oflags, ...) filep = fs_getfilep(fd); if (!filep) { /* The errno value has already been set */ - + leave_cancellation_point(); return ERROR; } @@ -203,6 +207,7 @@ int open(const char *path, int oflags, ...) goto errout_with_fd; } + leave_cancellation_point(); return fd; errout_with_fd: @@ -211,5 +216,6 @@ errout_with_inode: inode_release(inode); errout: set_errno(ret); + leave_cancellation_point(); return ERROR; } diff --git a/os/fs/vfs/fs_poll.c b/os/fs/vfs/fs_poll.c index f7b3c3a..deb9d5f 100644 --- a/os/fs/vfs/fs_poll.c +++ b/os/fs/vfs/fs_poll.c @@ -65,6 +65,7 @@ #include #include +#include #include #ifdef CONFIG_NET_LWIP @@ -303,6 +304,9 @@ int poll(FAR struct pollfd *fds, nfds_t nfds, int timeout) int count = 0; int ret; + /* poll() is a cancellation point */ + (void)enter_cancellation_point(); + sem_init(&sem, 0, 0); ret = poll_setup(fds, nfds, &sem); if (ret >= 0) { @@ -369,6 +373,7 @@ int poll(FAR struct pollfd *fds, nfds_t nfds, int timeout) } sem_destroy(&sem); + leave_cancellation_point(); /* Check for errors */ diff --git a/os/fs/vfs/fs_pread.c b/os/fs/vfs/fs_pread.c index 4b20441..126e9c9 100644 --- a/os/fs/vfs/fs_pread.c +++ b/os/fs/vfs/fs_pread.c @@ -60,6 +60,7 @@ #include #include +#include #include /**************************************************************************** @@ -157,6 +158,10 @@ ssize_t file_pread(FAR struct file *filep, FAR void *buf, size_t nbytes, off_t o ssize_t pread(int fd, FAR void *buf, size_t nbytes, off_t offset) { FAR struct file *filep; + ssize_t ret; + + /* pread() is a cancellation point */ + (void)enter_cancellation_point(); /* Get the file structure corresponding to the file descriptor. */ @@ -164,10 +169,12 @@ ssize_t pread(int fd, FAR void *buf, size_t nbytes, off_t offset) if (!filep) { /* The errno value has already been set */ - return (ssize_t)ERROR; + ret = (ssize_t)ERROR; + } else { + /* Let file_pread do the real work */ + ret = file_pread(filep, buf, nbytes, offset); } - /* Let file_pread do the real work */ - - return file_pread(filep, buf, nbytes, offset); + leave_cancellation_point(); + return ret; } diff --git a/os/fs/vfs/fs_pwrite.c b/os/fs/vfs/fs_pwrite.c index d728a8f..2888dbf 100644 --- a/os/fs/vfs/fs_pwrite.c +++ b/os/fs/vfs/fs_pwrite.c @@ -60,6 +60,7 @@ #include #include +#include #include /**************************************************************************** @@ -155,6 +156,10 @@ ssize_t file_pwrite(FAR struct file *filep, FAR const void *buf, size_t nbytes, ssize_t pwrite(int fd, FAR const void *buf, size_t nbytes, off_t offset) { FAR struct file *filep; + ssize_t ret; + + /* pwrite() is a cancellation point */ + (void)enter_cancellation_point(); /* Get the file structure corresponding to the file descriptor. */ @@ -162,10 +167,13 @@ ssize_t pwrite(int fd, FAR const void *buf, size_t nbytes, off_t offset) if (!filep) { /* The errno value has already been set */ - return (ssize_t)ERROR; - } + ret = (ssize_t)ERROR; + } else { + /* Let file_pread do the real work */ - /* Let file_pread do the real work */ + ret = file_pwrite(filep, buf, nbytes, offset); + } - return file_pwrite(filep, buf, nbytes, offset); + leave_cancellation_point(); + return ret; } diff --git a/os/fs/vfs/fs_read.c b/os/fs/vfs/fs_read.c index b0d6949..88583ab 100644 --- a/os/fs/vfs/fs_read.c +++ b/os/fs/vfs/fs_read.c @@ -62,6 +62,7 @@ #include #include #include +#include #include "inode/inode.h" @@ -150,6 +151,11 @@ ssize_t file_read(FAR struct file *filep, FAR void *buf, size_t nbytes) ssize_t read(int fd, FAR void *buf, size_t nbytes) { + ssize_t ret; + + /* read() is a cancellation point */ + (void)enter_cancellation_point(); + /* Did we get a valid file descriptor? */ #if CONFIG_NFILE_DESCRIPTORS > 0 @@ -157,16 +163,17 @@ ssize_t read(int fd, FAR void *buf, size_t nbytes) #endif { /* No.. If networking is enabled, read() is the same as recv() with - * the flags parameter set to zero. + * the flags parameter set to zero. Note that recv() sets + * the errno variables */ #if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0 - return recv(fd, buf, nbytes, 0); + ret = recv(fd, buf, nbytes, 0); #else /* No networking... it is a bad descriptor in any event */ set_errno(EBADF); - return ERROR; + ret = ERROR; #endif } #if CONFIG_NFILE_DESCRIPTORS > 0 @@ -174,19 +181,24 @@ ssize_t read(int fd, FAR void *buf, size_t nbytes) FAR struct file *filep; /* The descriptor is in a valid range to file descriptor... do the - * read. First, get the file structure. + * read. First, get the file structure. Note that on failure, + * fs_getfilep() will set the errno variable. */ filep = fs_getfilep(fd); if (!filep) { /* The errno value has already been set */ - return ERROR; - } + ret = ERROR; + } else { - /* Then let file_read do all of the work */ + /* Then let file_read do all of the work */ - return file_read(filep, buf, nbytes); + ret = file_read(filep, buf, nbytes); + } } #endif + + leave_cancellation_point(); + return ret; } diff --git a/os/fs/vfs/fs_select.c b/os/fs/vfs/fs_select.c index 3eebb46..5ff728f 100644 --- a/os/fs/vfs/fs_select.c +++ b/os/fs/vfs/fs_select.c @@ -66,6 +66,7 @@ #include #include +#include #include #include "inode/inode.h" @@ -126,6 +127,9 @@ int select(int nfds, FAR fd_set *readfds, FAR fd_set *writefds, FAR fd_set *exce int ndx; int ret; + /* select() is a cancellation point */ + (void)enter_cancellation_point(); + /* How many pollfd structures do we need to allocate? */ /* Initialize the descriptor list for poll() */ @@ -146,6 +150,7 @@ int select(int nfds, FAR fd_set *readfds, FAR fd_set *writefds, FAR fd_set *exce pollset = (struct pollfd *)kmm_zalloc(npfds * sizeof(struct pollfd)); if (!pollset) { set_errno(ENOMEM); + leave_cancellation_point(); return ERROR; } } @@ -275,6 +280,7 @@ int select(int nfds, FAR fd_set *readfds, FAR fd_set *writefds, FAR fd_set *exce set_errno(errcode); } + leave_cancellation_point(); return ret; } diff --git a/os/fs/vfs/fs_write.c b/os/fs/vfs/fs_write.c index 71f78cb..366f8b4 100644 --- a/os/fs/vfs/fs_write.c +++ b/os/fs/vfs/fs_write.c @@ -62,6 +62,7 @@ #include #include +#include #include #include "inode/inode.h" @@ -87,7 +88,7 @@ ssize_t file_write(FAR struct file *filep, FAR const void *buf, size_t nbytes) { FAR struct inode *inode; - int ret; + ssize_t ret; int err; /* Was this file opened for write access? */ @@ -175,6 +176,10 @@ ssize_t write(int fd, FAR const void *buf, size_t nbytes) #if CONFIG_NFILE_DESCRIPTORS > 0 FAR struct file *filep; #endif + ssize_t ret; + + /* write() is a cancellation point */ + (void)enter_cancellation_point(); /* Did we get a valid file descriptor? */ @@ -185,26 +190,32 @@ ssize_t write(int fd, FAR const void *buf, size_t nbytes) /* Write to a socket descriptor is equivalent to send with flags == 0 */ #if (defined(CONFIG_NET_TCP) || defined(CONFIG_NET_LWIP)) && CONFIG_NSOCKET_DESCRIPTORS > 0 - return send(fd, buf, nbytes, 0); + ret = send(fd, buf, nbytes, 0); #else set_errno(EBADF); - return ERROR; + ret = ERROR; #endif } #if CONFIG_NFILE_DESCRIPTORS > 0 - /* The descriptor is in the right range to be a file descriptor... write - * to the file. - */ - - filep = fs_getfilep(fd); - if (!filep) { - /* The errno value has already been set */ - - return ERROR; + else { + /* The descriptor is in the right range to be a file descriptor... write + * to the file. Note that fs_getfilep() will set the errno on failure. + */ + + filep = fs_getfilep(fd); + if (!filep) { + /* The errno value has already been set */ + + ret = ERROR; + } else { + /* Perform the write operation using the file descriptor as an index. + * Note that file_write() will set the errno on failure. + */ + + ret = file_write(filep, buf, nbytes); + } } - - /* Perform the write operation using the file descriptor as an index */ - - return file_write(filep, buf, nbytes); #endif + leave_cancellation_point(); + return ret; } diff --git a/os/include/pthread.h b/os/include/pthread.h index dc78f5d..0ec26d0 100644 --- a/os/include/pthread.h +++ b/os/include/pthread.h @@ -142,11 +142,15 @@ #define PTHREAD_DEFAULT_PRIORITY 100 -/* Cancellation states returned by pthread_cancelstate() */ +/* Cancellation states used by pthread_setcancelstate() */ #define PTHREAD_CANCEL_ENABLE (0) #define PTHREAD_CANCEL_DISABLE (1) +/* Cancellation types used by pthread_setcanceltype() */ +#define PTHREAD_CANCEL_DEFERRED (0) +#define PTHREAD_CANCEL_ASYNCHRONOUS (1) + /* Thread return value when a pthread is canceled */ #define PTHREAD_CANCELED ((FAR void*)ERROR) @@ -293,6 +297,12 @@ typedef struct pthread_barrier_s pthread_barrier_t; typedef bool pthread_once_t; +#ifdef CONFIG_PTHREAD_CLEANUP +/* This type describes the pthread cleanup callback (non-standard) */ + +typedef CODE void (*pthread_cleanup_t)(FAR void *arg); +#endif + /* Forware references */ struct sched_param; /* Defined in sched.h */ @@ -360,6 +370,8 @@ int pthread_cancel(pthread_t thread); */ int pthread_setcancelstate(int state, FAR int *oldstate); +int pthread_setcanceltype(int type, FAR int *oldtype); + /** * @cond * @internal @@ -368,6 +380,13 @@ void pthread_testcancel(void); /** * @endcond */ + +/* A thread may set up cleanup functions to execut when the thread exits or is canceled. */ +#ifdef CONFIG_PTHREAD_CLEANUP +void pthread_cleanup_pop(int execute); +void pthread_cleanup_push(pthread_cleanup_t routine, FAR void *arg); +#endif + /* A thread can await termination of another thread and retrieve the return * value of the thread. */ diff --git a/os/include/sched.h b/os/include/sched.h index 5bfa954..ae723e1 100644 --- a/os/include/sched.h +++ b/os/include/sched.h @@ -87,6 +87,15 @@ #define SCHED_SPORADIC 3 /* Not supported */ #define SCHED_OTHER 4 /* Not supported */ + +/* Cancellation definitions *****************************************************/ +/* Cancellation states used by task_setcancelstate() */ +#define TASK_CANCEL_ENABLE (0) +#define TASK_CANCEL_DISABLE (1) +/* Cancellation types used by task_setcanceltype() */ +#define TASK_CANCEL_DEFERRED (0) +#define TASK_CANCEL_ASYNCHRONOUS (1) + /* Pthread definitions **********************************************************/ #define PTHREAD_KEYS_MAX CONFIG_NPTHREAD_KEYS @@ -217,6 +226,10 @@ int task_delete(pid_t pid); */ int task_restart(pid_t pid); +int task_setcancelstate(int state, FAR int *oldstate); +int task_setcanceltype(int type, FAR int *oldtype); +void task_testcancel(void); + /* Task Scheduling Interfaces (based on POSIX APIs) */ /** * @ingroup SCHED_KERNEL diff --git a/os/include/tinyara/cancelpt.h b/os/include/tinyara/cancelpt.h new file mode 100644 index 0000000..c27c8b3 --- /dev/null +++ b/os/include/tinyara/cancelpt.h @@ -0,0 +1,169 @@ +/**************************************************************************** + * + * 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. + * + ****************************************************************************/ +/**************************************************************************** + * include/tinyara/cancelpt.h + * Definitions related to cancellation points + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_TINYARA_CANCELPT_H +#define __INCLUDE_TINYARA_CANCELPT_H + +/**************************************************************************** + * 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 + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * 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. + * + ****************************************************************************/ + +#ifdef CONFIG_CANCELLATION_POINTS +bool enter_cancellation_point(void); +#else +# define enter_cancellation_point() false +#endif + +/**************************************************************************** + * 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 + * + ****************************************************************************/ + +#ifdef CONFIG_CANCELLATION_POINTS +void leave_cancellation_point(void); +#else +# define leave_cancellation_point() +#endif + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_TINYARA_CANCELPT_H */ diff --git a/os/include/tinyara/sched.h b/os/include/tinyara/sched.h index 4f4392d..71923e0 100644 --- a/os/include/tinyara/sched.h +++ b/os/include/tinyara/sched.h @@ -151,9 +151,17 @@ #define TCB_FLAG_TTYPE_PTHREAD (1 << TCB_FLAG_TTYPE_SHIFT) /* User pthread */ #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_PENDING (1 << 3) /* Bit 3: Pthread cancel is pending */ -#define TCB_FLAG_ROUND_ROBIN (1 << 4) /* Bit 4: Round robin sched enabled */ -#define TCB_FLAG_EXIT_PROCESSING (1 << 5) /* Bit 5: Exitting */ +#define TCB_FLAG_CANCEL_DEFERRED (1 << 3) /* Bit 3: Deferred (vs asynch) cancellation type */ +#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_ROUND_ROBIN (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 << 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 */ @@ -259,6 +267,16 @@ struct child_status_s { }; #endif +/* struct pthread_cleanup_s ******************************************************/ +/* This structure describes one element of the pthread cleanup stack */ + +#ifdef CONFIG_PTHREAD_CLEANUP +struct pthread_cleanup_s { + pthread_cleanup_t pc_cleaner; /* Cleanup callback address */ + FAR void *pc_arg; /* Argument that accompanies the callback */ +}; +#endif + /* struct dspace_s ***************************************************************/ /** @brief This structure describes a reference counted D-Space region. This must be a @@ -493,6 +511,9 @@ struct tcb_s { uint8_t task_state; /* Current state of the thread */ uint16_t flags; /* Misc. general status flags */ int16_t lockcount; /* 0=preemptable (not-locked) */ +#ifdef CONFIG_CANCELLATION_POINTS + int16_t cpcount; /* Nested cancellation point count */ +#endif #if CONFIG_RR_INTERVAL > 0 int timeslice; /* RR timeslice interval remaining */ @@ -509,6 +530,10 @@ struct tcb_s { FAR void *adj_stack_ptr; /* Adjusted stack_alloc_ptr for HW */ /* The initial stack pointer value */ +#ifdef CONFIG_MPU_STACKGUARD + FAR void *stack_guard; /* address of the stack guard */ + size_t guard_size; /* size of the guard region */ +#endif /* External Module Support *************************************************** */ #ifdef CONFIG_PIC @@ -604,6 +629,15 @@ struct pthread_tcb_s { pthread_addr_t arg; /* Startup argument */ FAR void *joininfo; /* Detach-able info to support join */ +#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. + */ + + uint8_t tos; + struct pthread_cleanup_s stack[CONFIG_PTHREAD_CLEANUP_STACKSIZE]; +#endif + /* POSIX Thread Specific Data ************************************************ */ #if CONFIG_NPTHREAD_KEYS > 0 diff --git a/os/kernel/Kconfig b/os/kernel/Kconfig index 2e02cce..11ce03b 100644 --- a/os/kernel/Kconfig +++ b/os/kernel/Kconfig @@ -430,8 +430,37 @@ config NPTHREAD_KEYS The number of items of thread- specific data that can be retained +config PTHREAD_CLEANUP + bool "pthread cleanup stack" + default n + ---help--- + Select to enable support for pthread exit cleanup stacks. This + enables the interfaces pthread_cleanup_push() and + pthread_cleanup_pop(). + +config PTHREAD_CLEANUP_STACKSIZE + int "pthread cleanup stack size" + default 1 + range 1 32 + depends on PTHREAD_CLEANUP + ---help--- + The maximum number of cleanup actions that may be pushed by + pthread_clean_push(). This setting will increase the size of EVERY + pthread task control block by about n * CONFIG_PTHREAD_CLEANUP_STACKSIZE + where n is the size of a pointer, 2* sizeof(uintptr_t), this would be + 8 for a CPU with 32-bit addressing and 4 for a CPU with 16-bit + addressing. + endmenu # Pthread Options +config CANCELLATION_POINTS + bool "Cancellation points" + default n + ---help--- + Enable POSIX cancellation points for pthread_cancel(). If selected, + cancellation points will also used with the () task_delete() API even if + pthreads are not enabled. + menu "Performance Monitoring" config SCHED_CPULOAD @@ -1013,6 +1042,9 @@ config PREAPP_STACKSIZE The size of the stack to allocate for the pre-application thread that is started as soon as the OS completes its initialization. +config MPU_STACKGAURD + default n + config PTHREAD_STACK_MIN int "Minimum pthread stack size" default 256 @@ -1025,3 +1057,9 @@ config PTHREAD_STACK_DEFAULT ---help--- Default pthread stack size endmenu # Stack size information + +comment "Kernel Latency utility" +source kernel/latency/Kconfig + +comment "Kernel Debug and Simulation" +source kernel/debug/Kconfig diff --git a/os/kernel/mqueue/mq_rcvinternal.c b/os/kernel/mqueue/mq_rcvinternal.c index 19a65e6..8eefef7 100644 --- a/os/kernel/mqueue/mq_rcvinternal.c +++ b/os/kernel/mqueue/mq_rcvinternal.c @@ -66,6 +66,7 @@ #include #include +#include #include "sched/sched.h" #include "mqueue/mqueue.h" @@ -174,6 +175,19 @@ 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; @@ -222,6 +236,7 @@ FAR struct mqueue_msg_s *mq_waitreceive(mqd_t mqdes) msgq->nmsgs--; } + leave_cancellation_point(); return rcvmsg; } @@ -306,6 +321,5 @@ ssize_t mq_doreceive(mqd_t mqdes, FAR struct mqueue_msg_s *mqmsg, FAR char *ubuf } /* Return the length of the message transferred to the user buffer */ - return rcvmsglen; } diff --git a/os/kernel/mqueue/mq_receive.c b/os/kernel/mqueue/mq_receive.c index b7ef6fa..579151c 100644 --- a/os/kernel/mqueue/mq_receive.c +++ b/os/kernel/mqueue/mq_receive.c @@ -62,6 +62,7 @@ #include #include #include +#include #include "mqueue/mqueue.h" @@ -141,7 +142,11 @@ ssize_t mq_receive(mqd_t mqdes, FAR char *msg, size_t msglen, FAR int *prio) * errno appropriately. */ + /* mq_receive() is a cancellation point */ + (void)enter_cancellation_point(); + if (mq_verifyreceive(mqdes, msg, msglen) != OK) { + leave_cancellation_point(); return ERROR; } @@ -177,5 +182,6 @@ ssize_t mq_receive(mqd_t mqdes, FAR char *msg, size_t msglen, FAR int *prio) } sched_unlock(); + leave_cancellation_point(); return ret; } diff --git a/os/kernel/mqueue/mq_send.c b/os/kernel/mqueue/mq_send.c index 0f5d982..34cc74f 100644 --- a/os/kernel/mqueue/mq_send.c +++ b/os/kernel/mqueue/mq_send.c @@ -62,6 +62,7 @@ #include #include +#include #include "mqueue/mqueue.h" @@ -143,7 +144,11 @@ int mq_send(mqd_t mqdes, FAR const char *msg, size_t msglen, int prio) * on any failures to verify. */ + /* mq_send() is a cancellation point */ + (void)enter_cancellation_point(); + if (mq_verifysend(mqdes, msg, msglen, prio) != OK) { + leave_cancellation_point(); return ERROR; } @@ -190,5 +195,6 @@ int mq_send(mqd_t mqdes, FAR const char *msg, size_t msglen, int prio) } sched_unlock(); + leave_cancellation_point(); return ret; } diff --git a/os/kernel/mqueue/mq_sndinternal.c b/os/kernel/mqueue/mq_sndinternal.c index e4d0cf0..1ac9b82 100644 --- a/os/kernel/mqueue/mq_sndinternal.c +++ b/os/kernel/mqueue/mq_sndinternal.c @@ -68,6 +68,7 @@ #include #include #include +#include #include "sched/sched.h" #ifndef CONFIG_DISABLE_SIGNALS #include "signal/signal.h" @@ -252,6 +253,18 @@ 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; @@ -267,6 +280,7 @@ int mq_waitsend(mqd_t mqdes) /* No... We will return an error to the caller. */ set_errno(EAGAIN); + leave_cancellation_point(); return ERROR; } @@ -298,11 +312,13 @@ int mq_waitsend(mqd_t mqdes) */ if (get_errno() != OK) { + leave_cancellation_point(); return ERROR; } } } } + leave_cancellation_point(); return OK; } diff --git a/os/kernel/mqueue/mq_timedreceive.c b/os/kernel/mqueue/mq_timedreceive.c index ff8ec3f..9f62b49 100644 --- a/os/kernel/mqueue/mq_timedreceive.c +++ b/os/kernel/mqueue/mq_timedreceive.c @@ -66,6 +66,7 @@ #include #include +#include #include "sched/sched.h" #include "clock/clock.h" @@ -204,16 +205,21 @@ ssize_t mq_timedreceive(mqd_t mqdes, FAR char *msg, size_t msglen, FAR int *prio DEBUGASSERT(up_interrupt_context() == false && rtcb->waitdog == NULL); + /* mq_timedreceive() is not a cancellation point */ + (void)enter_cancellation_point(); + /* Verify the input parameters and, in case of an error, set * errno appropriately. */ if (mq_verifyreceive(mqdes, msg, msglen) != OK) { + leave_cancellation_point(); return ERROR; } if (!abstime || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) { set_errno(EINVAL); + leave_cancellation_point(); return ERROR; } @@ -225,6 +231,7 @@ ssize_t mq_timedreceive(mqd_t mqdes, FAR char *msg, size_t msglen, FAR int *prio rtcb->waitdog = wd_create(); if (!rtcb->waitdog) { set_errno(EINVAL); + leave_cancellation_point(); return ERROR; } @@ -272,6 +279,7 @@ ssize_t mq_timedreceive(mqd_t mqdes, FAR char *msg, size_t msglen, FAR int *prio sched_unlock(); wd_delete(rtcb->waitdog); rtcb->waitdog = NULL; + leave_cancellation_point(); return ERROR; } @@ -309,5 +317,6 @@ ssize_t mq_timedreceive(mqd_t mqdes, FAR char *msg, size_t msglen, FAR int *prio sched_unlock(); wd_delete(rtcb->waitdog); rtcb->waitdog = NULL; + leave_cancellation_point(); return ret; } diff --git a/os/kernel/mqueue/mq_timedsend.c b/os/kernel/mqueue/mq_timedsend.c index 4d2e710..49c3c36 100644 --- a/os/kernel/mqueue/mq_timedsend.c +++ b/os/kernel/mqueue/mq_timedsend.c @@ -66,6 +66,7 @@ #include #include +#include #include "clock/clock.h" #include "sched/sched.h" @@ -206,16 +207,21 @@ int mq_timedsend(mqd_t mqdes, FAR const char *msg, size_t msglen, int prio, FAR DEBUGASSERT(up_interrupt_context() == false && rtcb->waitdog == NULL); + /* mq_timedsend() is a cancellation point */ + (void)enter_cancellation_point(); + /* Verify the input parameters -- setting errno appropriately * on any failures to verify. */ if (mq_verifysend(mqdes, msg, msglen, prio) != OK) { + leave_cancellation_point(); return ERROR; } if (!abstime || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) { set_errno(EINVAL); + leave_cancellation_point(); return ERROR; } @@ -231,6 +237,7 @@ int mq_timedsend(mqd_t mqdes, FAR const char *msg, size_t msglen, int prio, FAR rtcb->waitdog = wd_create(); if (!rtcb->waitdog) { set_errno(EINVAL); + leave_cancellation_point(); return ERROR; } @@ -320,5 +327,6 @@ int mq_timedsend(mqd_t mqdes, FAR const char *msg, size_t msglen, int prio, FAR sched_unlock(); wd_delete(rtcb->waitdog); rtcb->waitdog = NULL; + leave_cancellation_point(); return ret; } diff --git a/os/kernel/pthread/Make.defs b/os/kernel/pthread/Make.defs index 3729fab..22e787f 100644 --- a/os/kernel/pthread/Make.defs +++ b/os/kernel/pthread/Make.defs @@ -68,6 +68,10 @@ ifneq ($(CONFIG_DISABLE_SIGNALS),y) CSRCS += pthread_condtimedwait.c pthread_kill.c pthread_sigmask.c endif +ifeq ($(CONFIG_PTHREAD_CLEANUP),y) +CSRCS += pthread_cleanup.c +endif + # Include pthread build support DEPPATH += --dep-path pthread diff --git a/os/kernel/pthread/pthread.h b/os/kernel/pthread/pthread.h index 4d9e766..e17c643 100644 --- a/os/kernel/pthread/pthread.h +++ b/os/kernel/pthread/pthread.h @@ -114,6 +114,9 @@ struct task_group_s; /* Forward reference */ void weak_function pthread_initialize(void); int pthread_schedsetup(FAR struct pthread_tcb_s *tcb, int priority, start_t start, pthread_startroutine_t entry); +#ifdef CONFIG_PTHREAD_CLEANUP +void pthread_cleanup_popall(FAR struct pthread_tcb_s *tcb); +#endif 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); diff --git a/os/kernel/pthread/pthread_cancel.c b/os/kernel/pthread/pthread_cancel.c index 4128349..240f2b4 100644 --- a/os/kernel/pthread/pthread_cancel.c +++ b/os/kernel/pthread/pthread_cancel.c @@ -62,6 +62,7 @@ #include #include "sched/sched.h" +#include "task/task.h" #include "pthread/pthread.h" /************************************************************************** @@ -94,24 +95,22 @@ int pthread_cancel(pthread_t thread) { - struct tcb_s *tcb; + struct pthread_tcb_s *tcb; /* First, make sure that the handle references a valid thread */ - if (!thread) { + if (thread == 0) { /* pid == 0 is the IDLE task. Callers cannot cancel the * IDLE task. */ - return ESRCH; } - tcb = sched_gettcb((pid_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; } @@ -121,7 +120,7 @@ int pthread_cancel(pthread_t thread) */ sched_lock(); - if ((tcb->flags & TCB_FLAG_NONCANCELABLE) != 0) { + if ((tcb->cmn.flags & TCB_FLAG_NONCANCELABLE) != 0) { /* Then we cannot cancel the thread now. Here is how this is * supposed to work: * @@ -135,10 +134,31 @@ int pthread_cancel(pthread_t thread) * processing." */ - tcb->flags |= TCB_FLAG_CANCEL_PENDING; + tcb->cmn.flags |= TCB_FLAG_CANCEL_PENDING; + sched_unlock(); + return OK; + } + +#ifdef CONFIG_CANCELLATION_POINTS + /* Check if this thread supports deferred cancellation */ + + if ((tcb->cmn.flags & TCB_FLAG_CANCEL_DEFERRED) != 0) { + /* If the thread is waiting at a cancellation point, then notify of the + * cancellation thereby waking the task up with an ECANCELED error. + * + * REVISIT: is locking the scheduler sufficent in SMP mode? + */ + + tcb->cmn.flags |= TCB_FLAG_CANCEL_PENDING; + + if (tcb->cmn.cpcount > 0) { + notify_cancellation(&tcb->cmn); + } + sched_unlock(); return OK; } +#endif sched_unlock(); @@ -146,16 +166,28 @@ int pthread_cancel(pthread_t thread) * same as pthread_exit(PTHREAD_CANCELED). */ - if (tcb == (struct tcb_s *)g_readytorun.head) { + if (tcb == (struct pthread_tcb_s *)g_readytorun.head) { pthread_exit(PTHREAD_CANCELED); } +#ifdef CONFIG_PTHREAD_CLEANUP + /* Perform any stack pthread clean-up callbacks. + * + * REVISIT: In this case, the clean-up callback will execute on the + * thread of the caller of pthread cancel, not on the thread of + * the thread-to-be-canceled. Is that an issue? Presumably they + * are both within the same group and within the same process address + * space. + */ + + pthread_cleanup_popall(tcb); +#endif + /* Complete pending join operations */ (void)pthread_completejoin((pid_t)thread, PTHREAD_CANCELED); - /* Then let pthread_delete do the real work */ + /* Then let task_terminate do the real work */ - task_delete((pid_t)thread); - return OK; + return task_terminate((pid_t)thread, false); } diff --git a/os/kernel/pthread/pthread_cleanup.c b/os/kernel/pthread/pthread_cleanup.c new file mode 100644 index 0000000..95fc4de --- /dev/null +++ b/os/kernel/pthread/pthread_cleanup.c @@ -0,0 +1,238 @@ +/**************************************************************************** + * + * 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_cleanup.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include + +#include + +#include "sched/sched.h" +#include "pthread/pthread.h" + +#ifdef CONFIG_PTHREAD_CLEANUP + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pthread_cleanup_pop_tcb + * + * Description: + * The pthread_cleanup_pop_tcb() function will remove the routine at the top + * of the calling thread's cancellation cleanup stack and optionally + * invoke it (if 'execute' is non-zero). + * + * Input Parameters: + * tcb - The TCB of the pthread that is exiting or being canceled. + * + * Return Value: + * None + * + * Assumptions: + * The scheduler is locked. + * + ****************************************************************************/ + +static void pthread_cleanup_pop_tcb(FAR struct pthread_tcb_s *tcb, int execute) +{ + if (tcb->tos > 0) { + unsigned int ndx; + + /* Get the index to the last cleaner function pushed onto the stack */ + + ndx = tcb->tos - 1; + DEBUGASSERT(ndx >= 0 && ndx < CONFIG_PTHREAD_CLEANUP_STACKSIZE); + + /* Should we execute the cleanup routine at the top of the stack? */ + + if (execute != 0) { + FAR struct pthread_cleanup_s *cb; + + /* Yes.. Execute the clean-up routine. + * + * REVISIT: This is a security problem In the PROTECTED and KERNEL + * builds: We must not call the registered function in supervisor + * mode! See also on_exit() and atexit() callbacks. + */ + + cb = &tcb->stack[ndx]; + cb->pc_cleaner(cb->pc_arg); + } + + tcb->tos = ndx; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pthread_cleanup_push + * pthread_cleanup_pop + * + * Description: + * The pthread_cleanup_pop() function will remove the routine at the top + * of the calling thread's cancellation cleanup stack and optionally + * invoke it (if 'execute' is non-zero). + * + * The pthread_cleanup_push() function will push the specified cancellation + * cleanup handler routine onto the calling thread's cancellation cleanup + * stack. The cancellation cleanup handler will be popped from the + * cancellation cleanup stack and invoked with the argument arg when: + * + * - The thread exits (that is, calls pthread_exit()). + * - The thread acts upon a cancellation request. + * - The thread calls pthread_cleanup_pop() with a non-zero execute argument. + * + * Input Parameters: + * routine - The cleanup routine to be pushed on the the cleanup stack. + * arg - An argument that will accompany the callback. + * execute - Execute the popped cleanup function immediately. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void pthread_cleanup_pop(int execute) +{ + FAR struct pthread_tcb_s *tcb = (FAR struct pthread_tcb_s *)g_readytorun.head; + + /* We don't assert if called from a non-pthread; we just don't do anything */ + + DEBUGASSERT(tcb != NULL); + + /* sched_lock() should provide sufficient protection. We only need to + * have this TCB stationary; the pthread cleanup stack should never be + * modified by interrupt level logic. + */ + + sched_lock(); + if ((tcb->cmn.flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD) { + pthread_cleanup_pop_tcb(tcb, execute); + } + + sched_unlock(); +} + +void pthread_cleanup_push(pthread_cleanup_t routine, FAR void *arg) +{ + FAR struct pthread_tcb_s *tcb = (FAR struct pthread_tcb_s *)g_readytorun.head; + + /* We don't assert if called from a non-pthread; we just don't do anything */ + + DEBUGASSERT(tcb != NULL); + DEBUGASSERT(tcb->tos < CONFIG_PTHREAD_CLEANUP_STACKSIZE); + + /* sched_lock() should provide sufficient protection. We only need to + * have this TCB stationary; the pthread cleanup stack should never be + * modified by interrupt level logic. + */ + + sched_lock(); + if ((tcb->cmn.flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD && + tcb->tos < CONFIG_PTHREAD_CLEANUP_STACKSIZE) { + unsigned int ndx = tcb->tos; + + tcb->tos++; + tcb->stack[ndx].pc_cleaner = routine; + tcb->stack[ndx].pc_arg = arg; + } + + sched_unlock(); +} + +/**************************************************************************** + * Name: pthread_cleanup_popall + * + * Description: + * The pthread_cleanup_popall() is an internal function that will pop and + * execute all clean-up functions. This function is only called from within + * the pthread_exit() and pthread_cancellation() logic + * + * Input Parameters: + * tcb - The TCB of the pthread that is exiting or being canceled. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void pthread_cleanup_popall(FAR struct pthread_tcb_s *tcb) +{ + DEBUGASSERT(tcb != NULL); + DEBUGASSERT((tcb->cmn.flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD); + + /* Pop and execute each cleanup routine/ + * + * sched_lock() should provide sufficient protection. We only need to + * have this TCB stationary; the pthread cleanup stack should never be + * modified by interrupt level logic. + */ + + sched_lock(); + while (tcb->tos > 0) { + pthread_cleanup_pop_tcb(tcb, 1); + } + + sched_unlock(); +} + +#endif /* CONFIG_PTHREAD_CLEANUP */ diff --git a/os/kernel/pthread/pthread_condtimedwait.c b/os/kernel/pthread/pthread_condtimedwait.c index e4df029..42ffec8 100644 --- a/os/kernel/pthread/pthread_condtimedwait.c +++ b/os/kernel/pthread/pthread_condtimedwait.c @@ -65,6 +65,7 @@ #include #include #include +#include #include @@ -206,6 +207,9 @@ int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex, DEBUGASSERT(rtcb->waitdog == NULL); + /* pthread_cond_timedwait() is a cancellation point */ + (void)enter_cancellation_point(); + /* Make sure that non-NULL references were provided. */ if (!cond || !mutex) { @@ -343,5 +347,6 @@ int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex, } svdbg("Returning %d\n", ret); + leave_cancellation_point(); return ret; } diff --git a/os/kernel/pthread/pthread_condwait.c b/os/kernel/pthread/pthread_condwait.c index 45b8579..af0f87e 100644 --- a/os/kernel/pthread/pthread_condwait.c +++ b/os/kernel/pthread/pthread_condwait.c @@ -62,6 +62,7 @@ #include #include +#include #include "pthread/pthread.h" /**************************************************************************** @@ -110,6 +111,9 @@ int pthread_cond_wait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex) svdbg("cond=0x%p mutex=0x%p\n", cond, mutex); + /* pthread_cond_wait() is a cancellation point */ + (void)enter_cancellation_point(); + /* Make sure that non-NULL references were provided. */ if (!cond || !mutex) { @@ -144,5 +148,6 @@ int pthread_cond_wait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex) } svdbg("Returning %d\n", ret); + leave_cancellation_point(); return ret; } diff --git a/os/kernel/pthread/pthread_create.c b/os/kernel/pthread/pthread_create.c index b000d24..a5a6159 100644 --- a/os/kernel/pthread/pthread_create.c +++ b/os/kernel/pthread/pthread_create.c @@ -382,6 +382,10 @@ int pthread_create(FAR pthread_t *thread, FAR const pthread_attr_t *attr, pthrea #endif } +#ifdef CONFIG_CANCELLATION_POINTS + /* Set the deferred cancellation types */ + ptcb->cmn.flags |= TCB_FLAG_CANCEL_DEFERRED; +#endif /* Get the assigned pid before we start the task (who knows what * could happen to ptcb after this!). Copy this ID into the join structure * as well. @@ -432,7 +436,6 @@ int pthread_create(FAR pthread_t *thread, FAR const pthread_attr_t *attr, pthrea errcode = EIO; goto errout_with_join; } - return ret; errout_with_join: diff --git a/os/kernel/pthread/pthread_exit.c b/os/kernel/pthread/pthread_exit.c index b9ba654..b2eb058 100644 --- a/os/kernel/pthread/pthread_exit.c +++ b/os/kernel/pthread/pthread_exit.c @@ -128,6 +128,21 @@ void pthread_exit(FAR void *exit_value) } #endif +#ifdef CONFIG_CANCELLATION_POINTS + /* Mark the pthread as non-cancelable to avoid additional calls to + * pthread_exit() due to any cancellation point logic that might get + * kicked off by actions taken during pthread_exit processing. + */ + tcb->flags |= TCB_FLAG_NONCANCELABLE; + tcb->flags &= ~TCB_FLAG_CANCEL_PENDING; + tcb->cpcount = 0; +#endif + +#ifdef CONFIG_PTHREAD_CLEANUP + /* Perform any stack pthread clean-up callbacks */ + pthread_cleanup_popall((FAR struct pthread_tcb_s *)tcb); +#endif + /* Complete pending join operations */ status = pthread_completejoin(getpid(), exit_value); diff --git a/os/kernel/pthread/pthread_join.c b/os/kernel/pthread/pthread_join.c index 497348e..9d4253c 100644 --- a/os/kernel/pthread/pthread_join.c +++ b/os/kernel/pthread/pthread_join.c @@ -54,9 +54,11 @@ * Included Files ****************************************************************************/ +#include #include #include #include +#include #include #include @@ -127,11 +129,15 @@ int pthread_join(pthread_t thread, FAR pthread_addr_t *pexit_value) svdbg("thread=%d group=%p\n", thread, group); DEBUGASSERT(group); + /* pthread_join() is a cancellation point */ + (void)enter_cancellation_point(); + /* First make sure that this is not an attempt to join to * ourself. */ if ((pid_t)thread == getpid()) { + leave_cancellation_point(); return EDEADLK; } @@ -256,5 +262,6 @@ int pthread_join(pthread_t thread, FAR pthread_addr_t *pexit_value) } svdbg("Returning %d\n", ret); + leave_cancellation_point(); return ret; } diff --git a/os/kernel/sched/sched_wait.c b/os/kernel/sched/sched_wait.c index 21457d3..78c095e 100644 --- a/os/kernel/sched/sched_wait.c +++ b/os/kernel/sched/sched_wait.c @@ -101,6 +101,9 @@ pid_t wait(FAR int *stat_loc) { + /* wait() is a cancellation point, but nothings needs to be done for this + * trivial case. + */ return waitpid((pid_t)-1, stat_loc, 0); } diff --git a/os/kernel/sched/sched_waitid.c b/os/kernel/sched/sched_waitid.c index 275e649..14d05c3 100644 --- a/os/kernel/sched/sched_waitid.c +++ b/os/kernel/sched/sched_waitid.c @@ -61,6 +61,7 @@ #include #include +#include #include "sched/sched.h" #include "group/group.h" @@ -179,6 +180,9 @@ int waitid(idtype_t idtype, id_t id, FAR siginfo_t *info, int options) int err; int ret; + /* waitid() is a 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 * know if the thread has child: The children remember their parents (if @@ -387,12 +391,14 @@ int waitid(idtype_t idtype, id_t id, FAR siginfo_t *info, int options) } } + leave_cancellation_point(); sched_unlock(); return OK; errout_with_errno: set_errno(err); errout: + leave_cancellation_point(); sched_unlock(); return ERROR; } diff --git a/os/kernel/sched/sched_waitpid.c b/os/kernel/sched/sched_waitpid.c index e1ef3b2..252e250 100644 --- a/os/kernel/sched/sched_waitpid.c +++ b/os/kernel/sched/sched_waitpid.c @@ -62,6 +62,7 @@ #include #include +#include #include "sched/sched.h" #include "group/group.h" @@ -207,11 +208,15 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) DEBUGASSERT(stat_loc); + /* waitpid() is a cancellation point */ + (void)enter_cancellation_point(); + /* None of the options are supported */ #ifdef CONFIG_DEBUG if (options != 0) { set_errno(ENOSYS); + leave_cancellation_point(); return ERROR; } #endif @@ -262,12 +267,14 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) /* On success, return the PID */ + leave_cancellation_point(); sched_unlock(); return pid; errout_with_errno: set_errno(err); errout: + leave_cancellation_point(); sched_unlock(); return ERROR; } @@ -301,11 +308,15 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) DEBUGASSERT(stat_loc); + /* waitpid() is a cancellation point */ + (void)enter_cancellation_point(); + /* None of the options are supported */ #ifdef CONFIG_DEBUG if (options != 0) { set_errno(ENOSYS); + leave_cancellation_point(); return ERROR; } #endif @@ -491,6 +502,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) } } + leave_cancellation_point(); sched_unlock(); return (int)pid; @@ -498,6 +510,7 @@ errout_with_errno: set_errno(err); errout_with_lock: + leave_cancellation_point(); sched_unlock(); return ERROR; } diff --git a/os/kernel/semaphore/sem_timedwait.c b/os/kernel/semaphore/sem_timedwait.c index 0a5b29c..30d6fa0 100644 --- a/os/kernel/semaphore/sem_timedwait.c +++ b/os/kernel/semaphore/sem_timedwait.c @@ -65,6 +65,7 @@ #include #include +#include #include "sched/sched.h" #include "clock/clock.h" @@ -188,6 +189,9 @@ int sem_timedwait(FAR sem_t *sem, FAR const struct timespec *abstime) DEBUGASSERT(up_interrupt_context() == false && rtcb->waitdog == NULL); + /* sem_timedwait() is a cancellation point */ + (void)enter_cancellation_point(); + /* Verify the input parameters and, in case of an error, set * errno appropriately. */ @@ -229,6 +233,7 @@ int sem_timedwait(FAR sem_t *sem, FAR const struct timespec *abstime) irqrestore(flags); wd_delete(rtcb->waitdog); rtcb->waitdog = NULL; + leave_cancellation_point(); return OK; } @@ -278,6 +283,7 @@ int sem_timedwait(FAR sem_t *sem, FAR const struct timespec *abstime) irqrestore(flags); wd_delete(rtcb->waitdog); rtcb->waitdog = NULL; + leave_cancellation_point(); /* We are either returning success or an error detected by sem_wait() * or the timeout detected by sem_timeout(). The 'errno' value has @@ -296,5 +302,6 @@ errout_disabled: errout: set_errno(err); + leave_cancellation_point(); return ERROR; } diff --git a/os/kernel/semaphore/sem_wait.c b/os/kernel/semaphore/sem_wait.c index 883b868..a42937e 100644 --- a/os/kernel/semaphore/sem_wait.c +++ b/os/kernel/semaphore/sem_wait.c @@ -61,10 +61,15 @@ #include #include #include +#include #include "sched/sched.h" #include "semaphore/semaphore.h" +#if defined(CONFIG_TINYARA_DEBUG) && defined(CONFIG_SEMAPHORE_HISTORY) +#include +#endif + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -119,18 +124,30 @@ int sem_wait(FAR sem_t *sem) /* This API should not be called from interrupt handlers */ - DEBUGASSERT(up_interrupt_context() == false) + DEBUGASSERT(up_interrupt_context() == false); /* Assume any errors reported are due to invalid arguments. */ set_errno(EINVAL); - if (sem) { - /* The following operations must be performed with interrupts - * disabled because sem_post() may be called from an interrupt - * handler. + /* The following operations must be performed with interrupts + * disabled because sem_post() may be called from an interrupt + * handler. + */ + saved_state = irqsave(); + + /* 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(); + irqrestore(saved_state); + return ERROR; + } - saved_state = irqsave(); + /* Make sure we were supplied with a valid semaphore */ + if (sem) { /* Check if the lock is available */ @@ -140,6 +157,9 @@ int sem_wait(FAR sem_t *sem) sem->semcount--; sem_addholder(sem); rtcb->waitsem = NULL; +#if defined(CONFIG_TINYARA_DEBUG) && defined(CONFIG_SEMAPHORE_HISTORY) + save_semaphore_history(sem, (void *)rtcb, SEM_AQUIRE); +#endif ret = OK; } @@ -162,6 +182,10 @@ int sem_wait(FAR sem_t *sem) rtcb->waitsem = sem; +#if defined(CONFIG_TINYARA_DEBUG) && defined(CONFIG_SEMAPHORE_HISTORY) + save_semaphore_history(sem, (void *)rtcb, SEM_WAITING); +#endif + /* If priority inheritance is enabled, then check the priority of * the holder of the semaphore. */ @@ -212,12 +236,10 @@ int sem_wait(FAR sem_t *sem) sched_unlock(); #endif } - - /* Interrupts may now be enabled. */ - - irqrestore(saved_state); } + leave_cancellation_point(); + irqrestore(saved_state); return ret; } @@ -253,18 +275,30 @@ int sem_wait_for_isr(FAR sem_t *sem) /* This API should not be called from interrupt handlers */ - DEBUGASSERT(up_interrupt_context() == false) + DEBUGASSERT(up_interrupt_context() == false); /* Assume any errors reported are due to invalid arguments. */ set_errno(EINVAL); - if (sem) { - /* The following operations must be performed with interrupts - * disabled because sem_post() may be called from an interrupt - * handler. + + /* The following operations must be performed with interrupts + * disabled because sem_post() may be called from an interrupt + * handler. + */ + saved_state = irqsave(); + + /* 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(); + irqrestore(saved_state); + return ERROR; + } - saved_state = irqsave(); + if (sem) { /* Check if the lock is available */ @@ -273,6 +307,9 @@ int sem_wait_for_isr(FAR sem_t *sem) sem->semcount--; rtcb->waitsem = NULL; +#if defined(CONFIG_TINYARA_DEBUG) && defined(CONFIG_SEMAPHORE_HISTORY) + save_semaphore_history(sem, (void *)rtcb, SEM_AQUIRE); +#endif ret = OK; } @@ -295,6 +332,10 @@ int sem_wait_for_isr(FAR sem_t *sem) rtcb->waitsem = sem; +#if defined(CONFIG_TINYARA_DEBUG) && defined(CONFIG_SEMAPHORE_HISTORY) + save_semaphore_history(sem, (void *)rtcb, SEM_WAITING); +#endif + /* Add the TCB to the prioritized semaphore wait queue */ set_errno(0); @@ -326,9 +367,10 @@ int sem_wait_for_isr(FAR sem_t *sem) } /* Interrupts may now be enabled. */ - - irqrestore(saved_state); } + leave_cancellation_point(); + irqrestore(saved_state); + return ret; } diff --git a/os/kernel/signal/sig_nanosleep.c b/os/kernel/signal/sig_nanosleep.c index 7636eed..b706044 100644 --- a/os/kernel/signal/sig_nanosleep.c +++ b/os/kernel/signal/sig_nanosleep.c @@ -63,6 +63,7 @@ #include #include +#include #include "clock/clock.h" @@ -149,6 +150,9 @@ int nanosleep(FAR const struct timespec *rqtp, FAR struct timespec *rmtp) int ret; #endif + /* nanosleep() is a cancellation point */ + (void)enter_cancellation_point(); + if (!rqtp || rqtp->tv_nsec < 0 || rqtp->tv_nsec >= 1000000000) { errval = EINVAL; goto errout; @@ -189,6 +193,7 @@ int nanosleep(FAR const struct timespec *rqtp, FAR struct timespec *rmtp) /* The timeout "error" is the normal, successful result */ irqrestore(flags); + leave_cancellation_point(); return OK; } @@ -229,5 +234,6 @@ int nanosleep(FAR const struct timespec *rqtp, FAR struct timespec *rmtp) errout: set_errno(errval); + leave_cancellation_point(); return ERROR; } diff --git a/os/kernel/signal/sig_pause.c b/os/kernel/signal/sig_pause.c index f7eb12d..64cb4f0 100644 --- a/os/kernel/signal/sig_pause.c +++ b/os/kernel/signal/sig_pause.c @@ -59,6 +59,8 @@ #include #include +#include + /**************************************************************************** * Preprocessor Definitions ****************************************************************************/ @@ -111,6 +113,10 @@ int pause(void) { sigset_t set; struct siginfo value; + int ret; + + /* pause() is a 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 @@ -123,5 +129,7 @@ int pause(void) * meaning that some unblocked signal was caught. */ - return sigwaitinfo(&set, &value); + ret = sigwaitinfo(&set, &value); + leave_cancellation_point(); + return ret; } diff --git a/os/kernel/signal/sig_suspend.c b/os/kernel/signal/sig_suspend.c index 2c857c2..a0b0633 100644 --- a/os/kernel/signal/sig_suspend.c +++ b/os/kernel/signal/sig_suspend.c @@ -62,6 +62,7 @@ #include #include +#include #include "sched/sched.h" #include "signal/signal.h" @@ -134,6 +135,9 @@ int sigsuspend(FAR const sigset_t *set) irqstate_t saved_state; int unblocksigno; + /* sigsuspend() is a 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 * be posted from the interrupt level, there is a race condition that @@ -187,5 +191,6 @@ int sigsuspend(FAR const sigset_t *set) } sched_unlock(); + leave_cancellation_point(); return ERROR; } diff --git a/os/kernel/signal/sig_timedwait.c b/os/kernel/signal/sig_timedwait.c index 0f2b689..fe01e3f 100644 --- a/os/kernel/signal/sig_timedwait.c +++ b/os/kernel/signal/sig_timedwait.c @@ -68,6 +68,7 @@ #include #include +#include #include "sched/sched.h" #include "signal/signal.h" @@ -197,6 +198,9 @@ int sigtimedwait(FAR const sigset_t *set, FAR struct siginfo *info, FAR const st DEBUGASSERT(rtcb->waitdog == NULL); + /* sigtimedwait() is a cancellation point */ + (void)enter_cancellation_point(); + sched_lock(); /* Not necessary */ /* Several operations must be performed below: We must determine if any @@ -350,5 +354,6 @@ int sigtimedwait(FAR const sigset_t *set, FAR struct siginfo *info, FAR const st } sched_unlock(); + leave_cancellation_point(); return ret; } diff --git a/os/kernel/signal/sig_waitinfo.c b/os/kernel/signal/sig_waitinfo.c index 99bbb46..4346274 100644 --- a/os/kernel/signal/sig_waitinfo.c +++ b/os/kernel/signal/sig_waitinfo.c @@ -56,6 +56,7 @@ #include #include +#include /**************************************************************************** * Definitions @@ -102,5 +103,12 @@ int sigwaitinfo(FAR const sigset_t *set, FAR struct siginfo *info) { - return sigtimedwait(set, info, NULL); + int ret; + + /* sigwaitinfo() is a cancellation point */ + (void)enter_cancellation_point(); + + ret = sigtimedwait(set, info, NULL); + leave_cancellation_point(); + return ret; } diff --git a/os/kernel/task/Make.defs b/os/kernel/task/Make.defs index cba28ab..f85ee78 100644 --- a/os/kernel/task/Make.defs +++ b/os/kernel/task/Make.defs @@ -54,7 +54,7 @@ CSRCS += task_create.c task_init.c task_setup.c task_activate.c CSRCS += task_start.c task_delete.c task_exit.c task_exithook.c CSRCS += task_recover.c task_restart.c task_spawnparms.c CSRCS += task_terminate.c task_getgroup.c task_prctl.c task_getpid.c -CSRCS += exit.c +CSRCS += exit.c task_setcancelstate.c ifeq ($(CONFIG_ARCH_HAVE_VFORK),y) ifeq ($(CONFIG_SCHED_WAITPID),y) @@ -78,6 +78,10 @@ ifeq ($(CONFIG_SCHED_ONEXIT),y) CSRCS += task_onexit.c endif +ifeq ($(CONFIG_CANCELLATION_POINTS),y) +CSRCS += task_setcanceltype.c task_testcancel.c task_cancelpt.c +endif + # Include task build support DEPPATH += --dep-path task diff --git a/os/kernel/task/task.h b/os/kernel/task/task.h index b23dc90..685b63c 100644 --- a/os/kernel/task/task.h +++ b/os/kernel/task/task.h @@ -97,4 +97,8 @@ void task_recover(FAR struct tcb_s *tcb); bool sched_addreadytorun(FAR struct tcb_s *rtrtcb); +#ifdef CONFIG_CANCELLATION_POINTS +void notify_cancellation(FAR struct tcb_s *tcb); +#endif + #endif /* __SCHED_TASK_TASK_H */ diff --git a/os/kernel/task/task_cancelpt.c b/os/kernel/task/task_cancelpt.c new file mode 100644 index 0000000..f6feba0 --- /dev/null +++ b/os/kernel/task/task_cancelpt.c @@ -0,0 +1,326 @@ +/**************************************************************************** + * + * 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/task/task_cancelpt.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * 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 + +#include +#include + +#include +#include + +#include "sched/sched.h" +#include "semaphore/semaphore.h" +#include "mqueue/mqueue.h" +#include "task/task.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 tcb_s *tcb = (FAR struct tcb_s *)g_readytorun.head; + bool ret = false; + + /* Disabling pre-emption should provide sufficient protection. We only + * need the TCB to be stationary (no interrupt level modification is + * anticipated). + * + * REVISIT: is locking the scheduler sufficent in SMP mode? + */ + + 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) { + /* Check if there is a pending cancellation */ + + 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 +#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(); + 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 tcb_s *tcb = (FAR struct tcb_s *)g_readytorun.head; + + /* Disabling pre-emption should provide sufficient protection. We only + * need the TCB to be stationary (no interrupt level modification is + * anticipated). + * + * REVISIT: is locking the scheduler sufficent in SMP mode? + */ + + 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(PTHREAD_CANCELED); + } else +#endif + { + exit(EXIT_FAILURE); + } + } + } else { + /* We are not at the outermost nesting level. Just decrment the + * nesting level count. + */ + + tcb->cpcount--; + } + } + + sched_unlock(); +} + +/**************************************************************************** + * Name: notify_cancellation + * + * Description: + * Called by task_delete() or pthread_cancel() if the cancellation occurs + * while we the thread is within the cancellation point. This logic + * behaves much like sending a signal: It will cause waiting threads + * to wake up and terminated with ECANCELED. A call to + * leave_cancellation_point() whould then follow, causing the thread to + * exit. + * + ****************************************************************************/ + +void notify_cancellation(FAR struct tcb_s *tcb) +{ + irqstate_t flags; + + /* We need perform the following operations from within a critical section + * because it can compete with interrupt level activity. + */ + + flags = irqsave(); + + /* 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 + } + + irqrestore(flags); +} + +#endif /* CONFIG_CANCELLATION_POINTS */ diff --git a/os/kernel/task/task_delete.c b/os/kernel/task/task_delete.c index 8e73b8c..68b9980 100644 --- a/os/kernel/task/task_delete.c +++ b/os/kernel/task/task_delete.c @@ -57,6 +57,7 @@ #include #include +#include #include @@ -105,45 +106,119 @@ * redirected to exit(). This can only happen if a task calls task_delete() * in order to delete itself. * - * In fact, this function (and task_terminate) are the final functions - * called all task termination sequences. task_delete may be called - * from: - * - * - task_restart(), - * - pthread_cancel(), - * - and directly from user code. - * - * Other exit paths (exit(), _eixt(), and pthread_exit()) will go through - * task_terminate() + * This function obeys the semantics of pthread cancellation: task + * deletion is deferred if cancellation is disabled or if deferred + * cancellation is supported (with cancellation points enabled). * * Inputs: * pid - The task ID of the task to delete. A pid of zero * signifies the calling task. * * Return Value: - * OK on success; or ERROR on failure - * - * This function can fail if the provided pid does not correspond to a - * task (errno is not set) + * OK on success; or ERROR on failure with the errno variable set + * appropriately. * ****************************************************************************/ int task_delete(pid_t pid) { + FAR struct tcb_s *dtcb; FAR struct tcb_s *rtcb; + int ret; - /* Check if the task to delete is the calling task */ + /* Check if the task to delete is the calling task: PID=0 means to delete + * the calling task. In this case, task_delete() is much like exit() + * except that it obeys the cancellation semantics. + */ rtcb = (FAR struct tcb_s *)g_readytorun.head; - if (pid == 0 || pid == rtcb->pid) { - /* If it is, then what we really wanted to do was exit. Note that we - * don't bother to unlock the TCB since it will be going away. + if (pid == 0) { + pid = rtcb->pid; + } + + /* Get the TCB of the task to be deleted */ + + dtcb = (FAR struct tcb_s *)sched_gettcb(pid); + if (dtcb == NULL) { + /* The pid does not correspond to any known thread. The task + * has probably already exited. + */ + + set_errno(ESRCH); + return ERROR; + } + + /* Only tasks and kernel threads should use this interface */ + + DEBUGASSERT((dtcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD); + + /* Check to see if this task 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). + */ + + sched_lock(); + if ((dtcb->flags & TCB_FLAG_NONCANCELABLE) != 0) { + /* Then we cannot cancel the thread now. Here is how this is + * supposed to work: + * + * "When cancelability is disabled, all cancels are held pending + * in the target thread until the thread changes the cancelability. + * When cancelability is deferred, all cancels are held pending in + * the target thread until the thread changes the cancelability, calls + * a function which is a cancellation point or calls pthread_testcancel(), + * thus creating a cancellation point. When cancelability is asynchronous, + * all cancels are acted upon immediately, interrupting the thread with its + * processing." */ - exit(EXIT_SUCCESS); + dtcb->flags |= TCB_FLAG_CANCEL_PENDING; + sched_unlock(); + return OK; } - /* Then let task_terminate do the heavy lifting */ +#ifdef CONFIG_CANCELLATION_POINTS + /* Check if this task supports deferred cancellation */ + + if ((dtcb->flags & TCB_FLAG_CANCEL_DEFERRED) != 0) { + + /* If the task is waiting at a cancellation point, then notify of the + * cancellation thereby waking the task up with an ECANCELED error. + * + * REVISIT: is locking the scheduler sufficent in SMP mode? + */ + + dtcb->flags |= TCB_FLAG_CANCEL_PENDING; + + if (dtcb->cpcount > 0) { + notify_cancellation(dtcb); + } + + sched_unlock(); + return OK; + } +#endif + + /* Check if the task to delete is the calling task */ + + sched_unlock(); + if (pid == rtcb->pid) { + /* If it is, then what we really wanted to do was exit. Note that we + * don't bother to unlock the TCB since it will be going away. + */ + + exit(EXIT_SUCCESS); + } + + /* Otherwise, perform the asynchronous cancellation, letting + * task_terminate() do all of the heavy lifting. + */ + + ret = task_terminate(pid, false); + if (ret < 0) { + set_errno(-ret); + return ERROR; + } - return task_terminate(pid, false); + return OK; } diff --git a/os/kernel/task/task_exithook.c b/os/kernel/task/task_exithook.c index 86c56b2..5aeb719 100644 --- a/os/kernel/task/task_exithook.c +++ b/os/kernel/task/task_exithook.c @@ -607,6 +607,17 @@ void task_exithook(FAR struct tcb_s *tcb, int status, bool nonblocking) if ((tcb->flags & TCB_FLAG_EXIT_PROCESSING) != 0) { return; } + +#ifdef CONFIG_CANCELLATION_POINTS + /* Mark the task as non-cancelable to avoid additional calls to exit() + * due to any cancellation point logic that might get kicked off by + * actions taken during exit processing. + */ + tcb->flags |= TCB_FLAG_NONCANCELABLE; + tcb->flags &= ~TCB_FLAG_CANCEL_PENDING; + tcb->cpcount = 0; +#endif + #if defined(CONFIG_SCHED_ATEXIT) || defined(CONFIG_SCHED_ONEXIT) /* If exit function(s) were registered, call them now before we do any un- * initialization. diff --git a/os/kernel/task/task_restart.c b/os/kernel/task/task_restart.c index ffdc86b..f7086a8 100644 --- a/os/kernel/task/task_restart.c +++ b/os/kernel/task/task_restart.c @@ -211,7 +211,7 @@ int task_restart(pid_t pid) status = task_activate((FAR struct tcb_s *)tcb); if (status != OK) { - (void)task_delete(pid); + (void)task_terminate(pid, true); set_errno(-status); return ERROR; } diff --git a/os/kernel/task/task_setcancelstate.c b/os/kernel/task/task_setcancelstate.c new file mode 100644 index 0000000..82bd6f6 --- /dev/null +++ b/os/kernel/task/task_setcancelstate.c @@ -0,0 +1,166 @@ +/**************************************************************************** + * + * 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/task/task_setcancelstate.c + * + * Copyright (C) 2007, 2008, 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include +#include +#include + +#include "sched/sched.h" +#include "task/task.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: task_setcancelstate + * + * Description: + * The task_setcancelstate() function atomically both sets the calling + * task's cancelability state to the indicated state and returns the + * previous cancelability state at the location referenced by oldstate. + * Legal values for state are TASK_CANCEL_ENABLE and TASK_CANCEL_DISABLE. + * + * The cancelability 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 tcb_s *tcb = (FAR struct tcb_s *)g_readytorun.head; + int ret = OK; + + /* Suppress context changes for a bit so that the flags are stable. (the + * flags should not change in interrupt handling). + */ + + sched_lock(); + + /* Return the current state if so requrested */ + + if (oldstate != NULL) { + if ((tcb->flags & TCB_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 */ + + tcb->flags &= ~TCB_FLAG_NONCANCELABLE; + + /* Check if a cancellation was pending */ + + if ((tcb->flags & TCB_FLAG_CANCEL_PENDING) != 0) { +#ifdef CONFIG_CANCELLATION_POINTS + /* If we are using deferred cancellation? */ + + if ((tcb->flags & TCB_FLAG_CANCEL_DEFERRED) != 0) { + /* Yes.. If we are within a cancellation point, then + * notify of the cancellation. + */ + + if (tcb->cpcount > 0) { + notify_cancellation(tcb); + } + } else +#endif + { + /* No.. We are using asynchronous cancellation. If the + * cancellation was pending in this case, then just exit. + */ + + tcb->flags &= ~TCB_FLAG_CANCEL_PENDING; + +#ifndef CONFIG_DISABLE_PTHREAD + if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD) { + pthread_exit(PTHREAD_CANCELED); + } else +#endif + { + exit(EXIT_FAILURE); + } + } + } + } else if (state == TASK_CANCEL_DISABLE) { + /* Set the non-cancelable state */ + + tcb->flags |= TCB_FLAG_NONCANCELABLE; + } else { + set_errno(EINVAL); + ret = ERROR; + } + + sched_unlock(); + return ret; +} diff --git a/os/kernel/task/task_setcanceltype.c b/os/kernel/task/task_setcanceltype.c new file mode 100644 index 0000000..6f7a1cc --- /dev/null +++ b/os/kernel/task/task_setcanceltype.c @@ -0,0 +1,150 @@ +/**************************************************************************** + * + * 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/task/task_setcanceltype.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include +#include +#include + +#include "sched/sched.h" +#include "task/task.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: task_setcancelstate + * + * Description: + * The task_setcanceltype() function atomically both sets the calling + * thread's cancelability type to the indicated type and returns the + * previous cancelability type at the location referenced by oldtype + * Legal values for type are TASK_CANCEL_DEFERRED and + * TASK_CANCEL_ASYNCHRONOUS. + * + * The cancelability state and type of any newly created threads, + * including the thread in which main() was first invoked, are + * TASK_CANCEL_ENABLE and TASK_CANCEL_DEFERRED respectively. + * + ****************************************************************************/ + +int task_setcanceltype(int type, FAR int *oldtype) +{ + FAR struct tcb_s *tcb = (FAR struct tcb_s *)g_readytorun.head; + int ret = OK; + + /* Suppress context changes for a bit so that the flags are stable. (the + * flags should not change in interrupt handling). + */ + + sched_lock(); + + /* Return the current type if so requrested */ + + if (oldtype != NULL) { + if ((tcb->flags & TCB_FLAG_CANCEL_DEFERRED) != 0) { + *oldtype = TASK_CANCEL_DEFERRED; + } else { + *oldtype = TASK_CANCEL_ASYNCHRONOUS; + } + } + + /* Set the new cancellation type */ + + if (type == TASK_CANCEL_ASYNCHRONOUS) { + /* Clear the deferred cancellation bit */ + + tcb->flags &= ~TCB_FLAG_CANCEL_DEFERRED; + +#ifdef CONFIG_CANCELLATION_POINTS + /* If we just switched from deferred to asynchronous type and if a + * cancellation is pending, then exit now. + */ + + if ((tcb->flags & TCB_FLAG_CANCEL_PENDING) != 0 && + (tcb->flags & TCB_FLAG_NONCANCELABLE) == 0) { + tcb->flags &= ~TCB_FLAG_CANCEL_PENDING; + + /* Exit according to the type of the thread */ + +#ifndef CONFIG_DISABLE_PTHREAD + if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD) { + pthread_exit(PTHREAD_CANCELED); + } else +#endif + { + exit(EXIT_FAILURE); + } + } +#endif + } +#ifdef CONFIG_CANCELLATION_POINTS + else if (type == TASK_CANCEL_DEFERRED) { + /* Set the deferred cancellation type */ + + tcb->flags |= TCB_FLAG_CANCEL_DEFERRED; + } +#endif + else { + ret = EINVAL; + } + + sched_unlock(); + return ret; +} diff --git a/os/kernel/task/task_setup.c b/os/kernel/task/task_setup.c index 39c5fbb..ee21fa5 100644 --- a/os/kernel/task/task_setup.c +++ b/os/kernel/task/task_setup.c @@ -374,6 +374,11 @@ static int thread_schedsetup(FAR struct tcb_s *tcb, int priority, start_t start, tcb->flags &= ~TCB_FLAG_TTYPE_MASK; tcb->flags |= ttype; +#ifdef CONFIG_CANCELLATION_POINTS + /* Set the deferred cancellation type */ + tcb->flags |= TCB_FLAG_CANCEL_DEFERRED; +#endif + /* Save initial thread scheduling policy int the TCB */ #if CONFIG_RR_INTERVAL > 0 @@ -421,7 +426,6 @@ static int thread_schedsetup(FAR struct tcb_s *tcb, int priority, start_t start, tcb->task_state = TSTATE_TASK_INACTIVE; sched_unlock(); } - return ret; } diff --git a/os/kernel/task/task_testcancel.c b/os/kernel/task/task_testcancel.c new file mode 100644 index 0000000..0363071 --- /dev/null +++ b/os/kernel/task/task_testcancel.c @@ -0,0 +1,84 @@ +/**************************************************************************** + * + * 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/task/task_testcancel.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include + +#include + +#include "task/task.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: task_testcancel + * + * Description: + * The task_testcancel() function creates a cancellation point in the + * calling thread. The task_testcancel() function has no effect if + * cancelability is disabled + * + ****************************************************************************/ + +void task_testcancel(void) +{ + (void)enter_cancellation_point(); + leave_cancellation_point(); +}