| /* |
| * Copyright (c) 2004, Bull S.A.. All rights reserved. |
| * Created by: Sebastien Decugis |
| |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of version 2 of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it would be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, write the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| |
| * This sample test aims to check the following assertion: |
| * |
| * While a thread is blocked on a conditionnal variable, |
| * a dynamic binding exists between this conditionnal variable |
| * and the mutex which was the second argument. |
| * This dynamic binding stops existing when the last thread is unblocked. |
| * Even if the conditionnal variable can then be reused with another mutex, |
| * the threads which have been unblocked must still acquire |
| * the mutex they had associated with the conditionnal variable at call time. |
| * |
| |
| * The steps are: |
| * -> Create two mutexes m1 and m2 (errorcheck or recursive) |
| * -> Create a condition variable c depending on a bootlean b |
| * -> create N threads which will |
| * -> lock m1 |
| * -> wait or timedwait c, m1 (while b is false) |
| * -> check it owns m1 (check depends on the mutex type) |
| * -> lock m2 |
| * -> wait or timedwait c, m2 (while b is false) |
| * -> check it owns m2 |
| * -> mark this thread as terminate |
| * -> Once all threads are waiting on (c,m1), |
| * mark b as true then broadcast c until all threads are terminated. |
| */ |
| |
| #define _POSIX_C_SOURCE 200112L |
| |
| #ifndef WITHOUT_XOPEN |
| #define _XOPEN_SOURCE 600 |
| #endif |
| |
| #include <pthread.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <time.h> |
| |
| #include "../testfrmw/testfrmw.h" |
| #include "../testfrmw/testfrmw.c" |
| |
| #ifndef VERBOSE |
| #define VERBOSE 1 |
| #endif |
| |
| #define NTHREADS (100) |
| |
| #ifndef WITHOUT_ALTCLK |
| #define USE_ALTCLK /* make tests with MONOTONIC CLOCK if supported */ |
| #endif |
| |
| #ifndef WITHOUT_XOPEN |
| |
| struct _td { |
| pthread_mutex_t mtx1, mtx2; /* The two mutex m1 and m2 */ |
| pthread_cond_t cnd; /* The cond var c */ |
| char boolcnd; /* The boolean predicate b associated with c */ |
| int type; /* Type of mutex */ |
| clockid_t cid; /* Clock used by cond c */ |
| int started; /* # of threads which are already waiting */ |
| int stopped; /* # of threads which are terminated */ |
| } data; |
| |
| void *threaded(void *arg) |
| { |
| int ret; |
| |
| struct timespec ts; |
| |
| /* Prepare the timeout parameter */ |
| ret = clock_gettime(data.cid, &ts); |
| if (ret != 0) |
| UNRESOLVED(ret, "Unable to get time from clock"); |
| ts.tv_sec += 30; |
| |
| /* Lock m1 */ |
| ret = pthread_mutex_lock(&(data.mtx1)); |
| if (ret != 0) |
| UNRESOLVED(ret, "Unable tu lock m1 in thread"); |
| |
| /* Tell the parent this thread is started */ |
| data.started++; |
| |
| /* wait for the cond - bind the cond to the mutex m1 */ |
| do { |
| if (arg == (void *)0) |
| ret = pthread_cond_wait(&(data.cnd), &(data.mtx1)); |
| else |
| ret = pthread_cond_timedwait(&(data.cnd), &(data.mtx1), |
| &ts); |
| } while ((ret == 0) && (data.boolcnd == 0)); |
| if (ret != 0) |
| UNRESOLVED(ret, "First wait failed in thread"); |
| |
| /* Test ownership and unlock m1 */ |
| if (data.type == PTHREAD_MUTEX_RECURSIVE) { |
| ret = pthread_mutex_trylock(&(data.mtx1)); |
| if (ret != 0) |
| FAILED("Unable to re-lock recursive mutex after cond wait"); |
| ret = pthread_mutex_unlock(&(data.mtx1)); |
| if (ret != 0) |
| UNRESOLVED(ret, "Mutex unlock failed"); |
| } |
| |
| ret = pthread_mutex_unlock(&(data.mtx1)); |
| if (ret != 0) |
| FAILED("Unable to unlock m1 in thread - not owner?"); |
| |
| ret = pthread_mutex_unlock(&(data.mtx1)); |
| if (ret == 0) |
| FAILED("Unlocking an unlocked mutex succeeded"); |
| |
| /* Lock m2 */ |
| ret = pthread_mutex_lock(&(data.mtx2)); |
| if (ret != 0) |
| UNRESOLVED(ret, "Unable tu lock m2 in thread"); |
| |
| /* wait for the cond - bind the cond to the mutex m2 */ |
| do { |
| if (arg == (void *)0) |
| ret = pthread_cond_wait(&(data.cnd), &(data.mtx2)); |
| else |
| ret = pthread_cond_timedwait(&(data.cnd), &(data.mtx2), |
| &ts); |
| } while ((ret == 0) && (data.boolcnd == 0)); |
| if (ret != 0) |
| UNRESOLVED(ret, "Second wait failed in thread"); |
| |
| /* Mark the thread as terminated while we are protected by m2 */ |
| data.stopped++; |
| |
| /* Test ownership and unlock m2*/ |
| if (data.type == PTHREAD_MUTEX_RECURSIVE) { |
| ret = pthread_mutex_trylock(&(data.mtx2)); |
| if (ret != 0) |
| FAILED("Unable to re-lock recursive mutex after cond wait"); |
| ret = pthread_mutex_unlock(&(data.mtx2)); |
| if (ret != 0) |
| UNRESOLVED(ret, "Mutex unlock failed"); |
| } |
| ret = pthread_mutex_unlock(&(data.mtx2)); |
| if (ret != 0) |
| FAILED("Unable to unlock m2 in thread - not owner?"); |
| ret = pthread_mutex_unlock(&(data.mtx2)); |
| if (ret == 0) |
| FAILED("Unlocking an unlocked mutex succeeded"); |
| |
| return NULL; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int ret, i, j; |
| pthread_mutexattr_t ma; |
| pthread_condattr_t ca; |
| pthread_t th[NTHREADS]; |
| int loc_started, loc_stopped; |
| |
| long altclk_ok, pshared_ok; |
| |
| struct { |
| char altclk; /* Want to use alternative clock */ |
| char pshared; /* Want to use process-shared primitives */ |
| int type; /* mutex type */ |
| char *descr; /* Description of the case */ |
| |
| } scenar[] = { |
| {0, 0, PTHREAD_MUTEX_RECURSIVE , "Recursive mutex"}, |
| {0, 0, PTHREAD_MUTEX_ERRORCHECK, "Errorcheck mutex"}, |
| #ifdef USE_ALTCLK |
| {1, 0, PTHREAD_MUTEX_RECURSIVE , "Recursive mutex + altclock cond"}, |
| {1, 0, PTHREAD_MUTEX_ERRORCHECK, "Errorcheck mutex + altclock cond"}, |
| {1, 1, PTHREAD_MUTEX_RECURSIVE , "Recursive pshared mutex + altclock cond"}, |
| {1, 1, PTHREAD_MUTEX_ERRORCHECK, "Errorcheck pshared mutex + altclock cond"}, |
| #endif |
| {0, 1, PTHREAD_MUTEX_RECURSIVE , "Recursive pshared mutex"}, |
| {0, 1, PTHREAD_MUTEX_ERRORCHECK, "Errorcheck pshared mutex"}, |
| }; |
| |
| output_init(); |
| |
| /* Initialize the constants */ |
| altclk_ok = sysconf(_SC_CLOCK_SELECTION); |
| if (altclk_ok > 0) |
| altclk_ok = sysconf(_SC_MONOTONIC_CLOCK); |
| |
| #ifndef USE_ALTCLK |
| if (altclk_ok > 0) |
| output("Implementation supports the MONOTONIC CLOCK " |
| "but option is disabled in test.\n"); |
| #endif |
| |
| pshared_ok = sysconf(_SC_THREAD_PROCESS_SHARED); |
| |
| #if VERBOSE > 0 |
| output("Test starting\n"); |
| output(" Process-shared primitive %s be tested\n", |
| (pshared_ok > 0) ? "will" : "won't"); |
| output(" Alternative clock for cond %s be tested\n", |
| (altclk_ok > 0) ? "will" : "won't"); |
| #endif |
| |
| for (i = 0; i < (sizeof(scenar) / sizeof(scenar[0])); i++) { |
| #if VERBOSE > 1 |
| output("Starting test for %s\n", scenar[i].descr); |
| #endif |
| |
| /* Initialize the data structure */ |
| ret = pthread_mutexattr_init(&ma); |
| if (ret != 0) |
| UNRESOLVED(ret, "Mutex attribute object init failed"); |
| |
| ret = pthread_mutexattr_settype(&ma, scenar[i].type); |
| if (ret != 0) |
| UNRESOLVED(ret, "Unable to set mutex type"); |
| |
| if ((pshared_ok > 0) && (scenar[i].pshared != 0)) { |
| ret = pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED); |
| if (ret != 0) |
| UNRESOLVED(ret, "Unable to set mutex process-shared"); |
| } |
| |
| ret = pthread_condattr_init(&ca); |
| if (ret != 0) |
| UNRESOLVED(ret, "Cond attribute object init failed"); |
| |
| if ((pshared_ok > 0) && (scenar[i].pshared != 0)) { |
| ret = pthread_condattr_setpshared(&ca, PTHREAD_PROCESS_SHARED); |
| if (ret != 0) |
| UNRESOLVED(ret, "Unable to set cond process-shared"); |
| } |
| |
| #ifdef USE_ALTCLK |
| if ((altclk_ok > 0) && (scenar[i].altclk != 0)) { |
| ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC); |
| if (ret != 0) |
| UNRESOLVED(ret, "Unable to set alternative (monotonic) clock for cond"); |
| } |
| #endif |
| |
| ret = pthread_mutex_init(&(data.mtx1), &ma); |
| if (ret != 0) |
| UNRESOLVED(ret, "Unable to init mutex 1"); |
| |
| ret = pthread_mutex_init(&(data.mtx2), &ma); |
| if (ret != 0) |
| UNRESOLVED(ret, "Unable to init mutex 2"); |
| |
| ret = pthread_cond_init(&(data.cnd), &ca); |
| if (ret != 0) |
| UNRESOLVED(ret, "Unable to initialize condvar"); |
| |
| data.boolcnd = 0; |
| |
| ret = pthread_mutexattr_gettype(&ma, &(data.type)); |
| if (ret != 0) |
| UNRESOLVED(ret, "Unable to get type from mutex attr"); |
| |
| #ifdef USE_ALTCLK |
| ret = pthread_condattr_getclock(&ca, &(data.cid)); |
| if (ret != 0) |
| UNRESOLVED(ret, "Unable to get clock ID from cond attr"); |
| #else |
| data.cid = CLOCK_REALTIME; |
| #endif |
| |
| data.started = 0; |
| data.stopped = 0; |
| |
| /* Start the threads */ |
| #if VERBOSE > 1 |
| output("Initialization OK, starting threads\n"); |
| #endif |
| for (j = 0; j < NTHREADS; j++) { |
| ret = pthread_create(&th[j], NULL, threaded, (void *)(long)(j & 1)); |
| if (ret != 0) |
| UNRESOLVED(ret, "Thread creation failed"); |
| } |
| |
| /* Wait for the threads to be started */ |
| do { |
| ret = pthread_mutex_lock(&(data.mtx1)); |
| if (ret != 0) |
| UNRESOLVED(ret, "Unable to lock m1 in parent"); |
| loc_started = data.started; |
| ret = pthread_mutex_unlock(&(data.mtx1)); |
| if (ret != 0) |
| UNRESOLVED(ret, "Unable to unlock m1 in parent"); |
| } while (loc_started < NTHREADS); |
| |
| /* Broadcast the condition until all threads are terminated */ |
| data.boolcnd = 1; |
| do { |
| ret = pthread_cond_broadcast(&(data.cnd)); |
| if (ret != 0) |
| UNRESOLVED(ret, "Unable to broadcast cnd"); |
| sched_yield(); |
| ret = pthread_mutex_lock(&(data.mtx2)); |
| if (ret != 0) |
| UNRESOLVED(ret, "Unable to lock m2 in parent"); |
| loc_stopped = data.stopped; |
| ret = pthread_mutex_unlock(&(data.mtx2)); |
| if (ret != 0) |
| UNRESOLVED(ret, "Unable to unlock m2 in parent"); |
| } while (loc_stopped < NTHREADS); |
| |
| /* Join the threads */ |
| for (j = 0; j < NTHREADS; j++) { |
| ret = pthread_join(th[j], NULL); |
| if (ret != 0) |
| UNRESOLVED(ret, "Thread join failed"); |
| } |
| |
| #if VERBOSE > 1 |
| output("Test passed for %s\n", scenar[i].descr); |
| #endif |
| |
| /* Destroy data */ |
| ret = pthread_cond_destroy(&(data.cnd)); |
| if (ret != 0) |
| UNRESOLVED(ret, "Cond destroy failed"); |
| |
| ret = pthread_mutex_destroy(&(data.mtx1)); |
| if (ret != 0) |
| UNRESOLVED(ret, "Mutex 1 destroy failed"); |
| |
| ret = pthread_mutex_destroy(&(data.mtx2)); |
| if (ret != 0) |
| UNRESOLVED(ret, "Mutex 2 destroy failed"); |
| |
| ret = pthread_condattr_destroy(&ca); |
| if (ret != 0) |
| UNRESOLVED(ret, "Cond attribute destroy failed"); |
| |
| ret = pthread_mutexattr_destroy(&ma); |
| if (ret != 0) |
| UNRESOLVED(ret, "Mutex attr destroy failed"); |
| } /* Proceed to next case */ |
| |
| PASSED; |
| } |
| |
| #else /* WITHOUT_XOPEN */ |
| int main(int argc, char *argv[]) |
| { |
| output_init(); |
| UNTESTED("This test requires XSI features"); |
| } |
| #endif |