blob: c31ff33a9db01833e4d41e2ecb1da8640f2c00f9 [file] [log] [blame]
/*
* 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