diff --git a/examples/ostest/Makefile b/examples/ostest/Makefile index 5006623fd..f533364c6 100644 --- a/examples/ostest/Makefile +++ b/examples/ostest/Makefile @@ -72,19 +72,24 @@ endif ifneq ($(CONFIG_DISABLE_PTHREAD),y) CSRCS += cancel.c cond.c mutex.c sem.c semtimed.c barrier.c timedwait.c CSRCS += pthread_rwlock.c +CSRCS += pthread_rwlock_cancel.c + +ifeq ($(CONFIG_PTHREAD_CLEANUP),y) +CSRCS += pthread_cleanup.c +endif ifneq ($(CONFIG_PTHREAD_MUTEX_UNSAFE),y) CSRCS += robust.c endif -ifeq ($(CONFIG_FS_NAMED_SEMAPHORES),y) -CSRCS += nsem.c -endif - ifeq ($(CONFIG_PTHREAD_MUTEX_TYPES),y) CSRCS += rmutex.c endif +ifeq ($(CONFIG_FS_NAMED_SEMAPHORES),y) +CSRCS += nsem.c +endif + ifneq ($(CONFIG_RR_INTERVAL),0) CSRCS += roundrobin.c endif diff --git a/examples/ostest/ostest.h b/examples/ostest/ostest.h index 7cd9458ed..b271d3f79 100644 --- a/examples/ostest/ostest.h +++ b/examples/ostest/ostest.h @@ -145,7 +145,7 @@ int waitpid_test(void); void mutex_test(void); -/* rmutex.c ******************************************************************/ +/* rmutex.c *****************************************************************/ void recursive_mutex_test(void); @@ -187,7 +187,7 @@ void robust_test(void); void timedwait_test(void); -/* sigprocmask.c ****************************************************************/ +/* sigprocmask.c ************************************************************/ void sigprocmask_test(void); @@ -208,7 +208,7 @@ void sigev_thread_test(void); void rr_test(void); -/* sporadic.c *************************************************************/ +/* sporadic.c ***************************************************************/ void sporadic_test(void); @@ -216,10 +216,18 @@ void sporadic_test(void); void tls_test(void); -/* pthread_rwlock.c ****************************************************************/ +/* pthread_rwlock.c *********************************************************/ void pthread_rwlock_test(void); +/* pthread_rwlock_cancel.c **************************************************/ + +void pthread_rwlock_cancel_test(void); + +/* pthread_cleanup.c ********************************************************/ + +void pthread_cleanup_test(void); + /* barrier.c ****************************************************************/ void barrier_test(void); diff --git a/examples/ostest/ostest_main.c b/examples/ostest/ostest_main.c index d623d8482..4cfe61cd7 100644 --- a/examples/ostest/ostest_main.c +++ b/examples/ostest/ostest_main.c @@ -430,15 +430,25 @@ static int user_main(int argc, char *argv[]) printf("\nuser_main: pthread_rwlock test\n"); pthread_rwlock_test(); check_test_memory_usage(); -#endif /* !CONFIG_DISABLE_PTHREAD */ -#ifndef CONFIG_DISABLE_PTHREAD + printf("\nuser_main: pthread_rwlock_cancel test\n"); + pthread_rwlock_cancel_test(); + check_test_memory_usage(); + +#ifdef CONFIG_PTHREAD_CLEANUP + /* Verify pthread cancellation cleanup handlers */ + + printf("\nuser_main: pthread_cleanup test\n"); + pthread_cleanup_test(); + check_test_memory_usage(); +#endif + /* Verify pthreads and condition variable timed waits */ printf("\nuser_main: timed wait test\n"); timedwait_test(); check_test_memory_usage(); -#endif +#endif /* !CONFIG_DISABLE_PTHREAD */ #if !defined(CONFIG_DISABLE_MQUEUE) && !defined(CONFIG_DISABLE_PTHREAD) /* Verify pthreads and message queues */ diff --git a/examples/ostest/pthread_cleanup.c b/examples/ostest/pthread_cleanup.c new file mode 100644 index 000000000..b6f018c05 --- /dev/null +++ b/examples/ostest/pthread_cleanup.c @@ -0,0 +1,165 @@ +/**************************************************************************** + * examples/ostest/pthread_cleanup.c + * + * Copyright (C) 2017 Haltian Ltd. All rights reserved. + * Author: Juha Niskanen + * + * 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 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct sync_s +{ + pthread_cond_t cond; + pthread_mutex_t lock; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void cleanup(FAR void * data) +{ + FAR struct sync_s *sync = (FAR struct sync_s *) data; + int status; + +#ifdef CONFIG_PTHREAD_MUTEX_UNSAFE + status = pthread_mutex_unlock(&sync->lock); + if (status != 0) + { + printf("pthread_cleanup: ERROR pthread_mutex_unlock in cleanup handler. " + "Status: %d\n", status); + } +#else + status = pthread_mutex_consistent(&sync->lock); + if (status != 0) + { + printf("pthread_cleanup: ERROR pthread_mutex_consistent in cleanup handler. " + "Status: %d\n", status); + } +#endif +} + +static void *cleanup_thread(FAR void * data) +{ + FAR struct sync_s *sync = (FAR struct sync_s *) data; + int status; + + status = pthread_mutex_lock(&sync->lock); + if (status != 0) + { + printf("pthread_cleanup: ERROR pthread_mutex_lock, status=%d\n", status); + return NULL; + } + + pthread_cleanup_push(&cleanup, sync); + + while(1) + { + status = pthread_cond_wait(&sync->cond, &sync->lock); + if (status != 0) + { + printf("pthread_cleanup: ERROR wait returned. Status: %d\n", status); + } + } + + pthread_cleanup_pop(1); + return NULL; +} + +static void test_cleanup(void) +{ + pthread_t thread1; + int status; + void *result; + struct sync_s sync = { + PTHREAD_COND_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER + }; + + status = pthread_create(&thread1, NULL, cleanup_thread, &sync); + if (status != 0) + { + printf("pthread_cleanup: ERROR pthread_create, status=%d\n", status); + return; + } + + usleep(500 * 1000); + + status = pthread_cancel(thread1); + if (status != 0) + { + printf("pthread_cleanup: ERROR pthread_cancel, status=%d\n", status); + } + + status = pthread_join(thread1, &result); + if (status != 0) + { + printf("pthread_cleanup: ERROR pthread_join, status=%d\n", status); + } + else if (result != PTHREAD_CANCELED) + { + printf("pthread_cleanup: ERROR pthread_join returned wrong result: %p\n", result); + } + + /* Do some operations on lock in order to check if it is in usable state. */ + + status = pthread_mutex_trylock(&sync.lock); + if (status != 0) + { + printf("pthread_cleanup: ERROR pthread_mutex_trylock, status=%d\n", status); + } + + status = pthread_mutex_unlock(&sync.lock); + if (status != 0) + { + printf("pthread_cleanup: ERROR pthread_mutex_unlock, status=%d\n", status); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void pthread_cleanup_test(void) +{ + printf("pthread_cleanup: Starting test\n"); + test_cleanup(); +} diff --git a/examples/ostest/pthread_rwlock.c b/examples/ostest/pthread_rwlock.c index a53242899..22ad446e8 100644 --- a/examples/ostest/pthread_rwlock.c +++ b/examples/ostest/pthread_rwlock.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/examples/pthread_rwlock.c + * examples/ostest/pthread_rwlock.c * * Copyright (C) 2017 Mark Schulte. All rights reserved. * Author: Mark Schulte @@ -423,14 +423,15 @@ void pthread_rwlock_test(void) status = pthread_rwlock_trywrlock(&rw_lock); if (status != EBUSY) { - printf("pthread_rwlock: ""ERROR able to acquire to write locks\n"); + printf("pthread_rwlock: " + "ERROR able to acquire write lock when write lock already acquired\n"); } status = pthread_rwlock_tryrdlock(&rw_lock); if (status != EBUSY) { printf("pthread_rwlock: " - "ERROR able to acquire read lock when read lock already acquired\n"); + "ERROR able to acquire read lock when write lock already acquired\n"); } status = pthread_rwlock_unlock(&rw_lock); diff --git a/examples/ostest/pthread_rwlock_cancel.c b/examples/ostest/pthread_rwlock_cancel.c new file mode 100644 index 000000000..ced4075d9 --- /dev/null +++ b/examples/ostest/pthread_rwlock_cancel.c @@ -0,0 +1,252 @@ +/**************************************************************************** + * examples/ostest/pthread_rwlock_cancel.c + * + * Copyright (C) 2017 Haltian Ltd. All rights reserved. + * Author: Juha Niskanen + * + * 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 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct sync_s +{ + pthread_rwlock_t *read_lock; + pthread_rwlock_t *write_lock; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void * timeout_thread1(FAR void * data) +{ + FAR struct sync_s * sync = (FAR struct sync_s *) data; + struct timespec time; + int status; + + while(1) + { + (void)clock_gettime(CLOCK_REALTIME, &time); + time.tv_sec += 1; + + status = pthread_rwlock_timedrdlock(sync->write_lock, &time); + if (status != ETIMEDOUT) + { + printf("pthread_rwlock_cancel: ERROR Acquired held write_lock. Status: %d\n", status); + } + } + + return NULL; +} + +static void * timeout_thread2(FAR void * data) +{ + FAR struct sync_s * sync = (FAR struct sync_s *) data; + struct timespec time; + int status; + + while (1) + { + (void)clock_gettime(CLOCK_REALTIME, &time); + time.tv_sec += 1; + + status = pthread_rwlock_timedrdlock(sync->read_lock, &time); + if (status != 0) + { + printf("pthread_rwlock_cancel: Failed to acquire read_lock. Status: %d\n", status); + } + + sched_yield(); /* Not a cancellation point. */ + + if (status == 0) + { + status = pthread_rwlock_unlock(sync->read_lock); + if (status != 0) + { + printf("pthread_rwlock_cancel: Failed to release read_lock. Status: %d\n", status); + } + } + + (void)clock_gettime(CLOCK_REALTIME, &time); + time.tv_sec += 1; + + status = pthread_rwlock_timedwrlock(sync->read_lock, &time); + if (status != ETIMEDOUT) + { + printf("pthread_rwlock_cancel: " + "ERROR Acquired held read_lock for writing. Status: %d\n", status); + } + } + + return NULL; +} + +static void test_timeout(void) +{ + pthread_rwlock_t read_lock; + pthread_rwlock_t write_lock; + struct sync_s sync; + pthread_t thread1, thread2; + int status, i; + + status = pthread_rwlock_init(&read_lock, NULL); + if (status != 0) + { + printf("pthread_rwlock_cancel: ERROR pthread_rwlock_init(read_lock), status=%d\n", + status); + } + + status = pthread_rwlock_init(&write_lock, NULL); + if (status != 0) + { + printf("pthread_rwlock_cancel: ERROR pthread_rwlock_init(write_lock), status=%d\n", + status); + } + + status = pthread_rwlock_rdlock(&read_lock); + if (status != 0) + { + printf("pthread_rwlock_cancel: ERROR pthread_rwlock_rdlock, status=%d\n", + status); + } + + status = pthread_rwlock_wrlock(&write_lock); + if (status != 0) + { + printf("pthread_rwlock_cancel: ERROR pthread_rwlock_wrlock, status=%d\n", + status); + } + + sync.read_lock = &read_lock; + sync.write_lock = &write_lock; + + status = pthread_create(&thread1, NULL, timeout_thread1, &sync); + if (status != 0) + { + printf("pthread_rwlock_cancel: ERROR pthread_create, status=%d\n", status); + } + + status = pthread_create(&thread2, NULL, timeout_thread2, &sync); + if (status != 0) + { + printf("pthread_rwlock_cancel: ERROR pthread_create, status=%d\n", status); + } + + for (i = 0; i < 10; i++) + { + usleep(300 * 1000); /* Give threads few seconds to run */ + } + + status = pthread_cancel(thread1); + if (status != 0) + { + printf("pthread_rwlock_cancel: ERROR pthread_cancel, status=%d\n", status); + } + + status = pthread_cancel(thread2); + if (status != 0) + { + printf("pthread_rwlock_cancel: ERROR pthread_cancel, status=%d\n", status); + } + + (void) pthread_join(thread1, NULL); + (void) pthread_join(thread2, NULL); + + /* Do some operations on locks in order to check if they are still in + * usable state after deferred cancellation. */ + +#ifdef CONFIG_PTHREAD_CLEANUP +#ifdef CONFIG_CANCELLATION_POINTS + status = pthread_rwlock_trywrlock(&write_lock); + if (status != EBUSY) + { + printf("pthread_rwlock_cancel: " + "ERROR able to acquire write lock when write lock already acquired, " + "status=%d\n", status); + } + + status = pthread_rwlock_tryrdlock(&write_lock); + if (status != EBUSY) + { + printf("pthread_rwlock_cancel: " + "ERROR able to acquire read lock when write lock already acquired, " + "status=%d\n", status); + } + + status = pthread_rwlock_unlock(&read_lock); + if (status != 0) + { + printf("pthread_rwlock_cancel: " + "ERROR pthread_rwlock_unlock, status=%d\n", status); + } + + status = pthread_rwlock_unlock(&write_lock); + if (status != 0) + { + printf("pthread_rwlock_cancel: " + "ERROR pthread_rwlock_unlock, status=%d\n", status); + } + + status = pthread_rwlock_rdlock(&read_lock); + if (status != 0) + { + printf("pthread_rwlock_cancel: " + "ERROR pthread_rwlock_rdlock, status=%d\n", status); + } + + status = pthread_rwlock_wrlock(&write_lock); + if (status != 0) + { + printf("pthread_rwlock_cancel: " + "ERROR pthread_rwlock_wrlock, status=%d\n", status); + } +#endif /* CONFIG_CANCELLATION_POINTS */ +#endif /* CONFIG_PTHREAD_CLEANUP */ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void pthread_rwlock_cancel_test(void) +{ + printf("pthread_rwlock_cancel: Starting test\n"); + test_timeout(); +}