1/*
2 * Copyright (c) 2004, Bull S.A..  All rights reserved.
3 * Created by: Sebastien Decugis
4
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write the Free Software Foundation, Inc., 59
15 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
16
17
18 * This sample test aims to check the following assertion:
19 *
20 *  While a thread is blocked on a conditionnal variable,
21 * a dynamic binding exists between this conditionnal variable
22 * and the mutex which was the second argument.
23 *  This dynamic binding stops existing when the last thread is unblocked.
24 *  Even if the conditionnal variable can then be reused with another mutex,
25 * the threads which have been unblocked must still acquire
26 * the mutex they had associated with the conditionnal variable at call time.
27 *
28
29 * The steps are:
30 * -> Create two mutexes m1 and m2 (errorcheck or recursive)
31 * -> Create a condition variable c depending on a bootlean b
32 * -> create N threads which will
33 *     -> lock m1
34 *     -> wait or timedwait c, m1 (while b is false)
35 *     -> check it owns m1 (check depends on the mutex type)
36 *     -> lock m2
37 *     -> wait or timedwait c, m2 (while b is false)
38 *     -> check it owns m2
39 *     -> mark this thread as terminate
40 * -> Once all threads are waiting on (c,m1),
41 *     mark b as true then broadcast c until all threads are terminated.
42 */
43
44 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
45 #define _POSIX_C_SOURCE 200112L
46
47 /* We need the XSI extention for the mutex attributes */
48#ifndef WITHOUT_XOPEN
49 #define _XOPEN_SOURCE	600
50#endif
51 /********************************************************************************************/
52/****************************** standard includes *****************************************/
53/********************************************************************************************/
54 #include <pthread.h>
55 #include <stdarg.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <unistd.h>
59
60 #include <time.h>
61
62/********************************************************************************************/
63/******************************   Test framework   *****************************************/
64/********************************************************************************************/
65 #include "testfrmw.h"
66 #include "testfrmw.c"
67 /* This header is responsible for defining the following macros:
68  * UNRESOLVED(ret, descr);
69  *    where descr is a description of the error and ret is an int (error code for example)
70  * FAILED(descr);
71  *    where descr is a short text saying why the test has failed.
72  * PASSED();
73  *    No parameter.
74  *
75  * Both three macros shall terminate the calling process.
76  * The testcase shall not terminate in any other maneer.
77  *
78  * The other file defines the functions
79  * void output_init()
80  * void output(char * string, ...)
81  *
82  * Those may be used to output information.
83  */
84
85/********************************************************************************************/
86/********************************** Configuration ******************************************/
87/********************************************************************************************/
88#ifndef VERBOSE
89#define VERBOSE 1
90#endif
91
92#define NTHREADS (100)
93
94#ifndef WITHOUT_ALTCLK
95#define USE_ALTCLK  /* make tests with MONOTONIC CLOCK if supported */
96#endif
97
98/********************************************************************************************/
99/***********************************    Test case   *****************************************/
100/********************************************************************************************/
101#ifndef WITHOUT_XOPEN
102
103struct _td
104{
105	pthread_mutex_t mtx1, mtx2; /* The two mutex m1 and m2 */
106	pthread_cond_t cnd;         /* The cond var c */
107	char boolcnd;               /* The boolean predicate b associated with c */
108	int type;                   /* Type of mutex */
109	clockid_t cid;              /* Clock used by cond c */
110	int started;                /* # of threads which are already waiting */
111	int stopped;                /* # of threads which are terminated */
112} data;
113
114void * threaded ( void * arg )
115{
116	int ret;
117
118	struct timespec ts;
119
120	/* Prepare the timeout parameter */
121	ret = clock_gettime(data.cid, &ts);
122	if (ret != 0)  {  UNRESOLVED(ret, "Unable to get time from clock");  }
123	ts.tv_sec += 30;
124
125	/* Lock m1 */
126	ret = pthread_mutex_lock(&(data.mtx1));
127	if (ret != 0)  {  UNRESOLVED(ret, "Unable tu lock m1 in thread");  }
128
129	/* Tell the parent this thread is started */
130	data.started++;
131
132	/* wait for the cond - bind the cond to the mutex m1 */
133	do
134	{
135		if (arg == (void *)0)
136			ret = pthread_cond_wait(&(data.cnd), &(data.mtx1));
137		else
138			ret = pthread_cond_timedwait(&(data.cnd), &(data.mtx1), &ts);
139	} while ((ret == 0) && (data.boolcnd == 0));
140	if (ret != 0) {  UNRESOLVED(ret, "First wait failed in thread");  }
141
142	/* Test ownership and unlock m1 */
143	if (data.type == PTHREAD_MUTEX_RECURSIVE)
144	{
145		ret = pthread_mutex_trylock(&(data.mtx1));
146		if (ret != 0)  {  FAILED("Unable to re-lock recursive mutex after cond wait");  }
147		ret = pthread_mutex_unlock(&(data.mtx1));
148		if (ret != 0)  {  UNRESOLVED(ret, "Mutex unlock failed");  }
149	}
150	ret = pthread_mutex_unlock(&(data.mtx1));
151	if (ret != 0) {  FAILED("Unable to unlock m1 in thread - not owner?");  }
152	ret = pthread_mutex_unlock(&(data.mtx1));
153	if (ret == 0) {  FAILED("Unlocking an unlocked mutex succeeded");  }  /* Failed while this is not a default mutex */
154
155	/* Lock m2 */
156	ret = pthread_mutex_lock(&(data.mtx2));
157	if (ret != 0)  {  UNRESOLVED(ret, "Unable tu lock m2 in thread");  }
158
159	/* wait for the cond - bind the cond to the mutex m2 */
160	do
161	{
162		if (arg == (void *)0)
163			ret = pthread_cond_wait(&(data.cnd), &(data.mtx2));
164		else
165			ret = pthread_cond_timedwait(&(data.cnd), &(data.mtx2), &ts);
166	} while ((ret == 0) && (data.boolcnd == 0));
167	if (ret != 0) {  UNRESOLVED(ret, "Second wait failed in thread");  }
168
169	/* Mark the thread as terminated while we are protected by m2 */
170	data.stopped++;
171
172	/* Test ownership and unlock m2*/
173	if (data.type == PTHREAD_MUTEX_RECURSIVE)
174	{
175		ret = pthread_mutex_trylock(&(data.mtx2));
176		if (ret != 0)  {  FAILED("Unable to re-lock recursive mutex after cond wait");  }
177		ret = pthread_mutex_unlock(&(data.mtx2));
178		if (ret != 0)  {  UNRESOLVED(ret, "Mutex unlock failed");  }
179	}
180	ret = pthread_mutex_unlock(&(data.mtx2));
181	if (ret != 0) {  FAILED("Unable to unlock m2 in thread - not owner?");  }
182	ret = pthread_mutex_unlock(&(data.mtx2));
183	if (ret == 0) {  FAILED("Unlocking an unlocked mutex succeeded");  }  /* Failed while this is not a default mutex */
184
185	return NULL;
186}
187
188
189int main(int argc, char * argv[])
190{
191	int ret, i, j;
192	pthread_mutexattr_t ma;
193	pthread_condattr_t ca;
194	pthread_t th[NTHREADS];
195	int loc_started, loc_stopped;
196
197	long altclk_ok, pshared_ok;
198
199	struct
200	{
201		char altclk;  /* Want to use alternative clock */
202		char pshared; /* Want to use process-shared primitives */
203		int type;     /* mutex type */
204		char * descr; /* Description of the case */
205
206	} scenar[] =
207	{ {0, 0, PTHREAD_MUTEX_RECURSIVE , "Recursive mutex"  }
208	 ,{0, 0, PTHREAD_MUTEX_ERRORCHECK, "Errorcheck mutex" }
209	#ifdef USE_ALTCLK
210	 ,{1, 0, PTHREAD_MUTEX_RECURSIVE , "Recursive mutex + altclock cond" }
211	 ,{1, 0, PTHREAD_MUTEX_ERRORCHECK, "Errorcheck mutex + altclock cond" }
212	 ,{1, 1, PTHREAD_MUTEX_RECURSIVE , "Recursive pshared mutex + altclock cond" }
213	 ,{1, 1, PTHREAD_MUTEX_ERRORCHECK, "Errorcheck pshared mutex + altclock cond" }
214	#endif
215	 ,{0, 1, PTHREAD_MUTEX_RECURSIVE , "Recursive pshared mutex" }
216	 ,{0, 1, PTHREAD_MUTEX_ERRORCHECK, "Errorcheck pshared mutex" }
217	};
218
219	output_init();
220
221	/* Initialize the constants */
222	altclk_ok = sysconf(_SC_CLOCK_SELECTION);
223	if (altclk_ok > 0)
224		altclk_ok = sysconf(_SC_MONOTONIC_CLOCK);
225
226#ifndef USE_ALTCLK
227	if (altclk_ok > 0)
228		output("Implementation supports the MONOTONIC CLOCK but option is disabled in test.\n");
229#endif
230
231
232	pshared_ok = sysconf(_SC_THREAD_PROCESS_SHARED);
233
234	#if VERBOSE > 0
235	output("Test starting\n");
236	output(" Process-shared primitive %s be tested\n", (pshared_ok>0)?"will":"won't");
237	output(" Alternative clock for cond %s be tested\n", (altclk_ok>0)?"will":"won't");
238	#endif
239
240	for (i=0; i< (sizeof(scenar) / sizeof(scenar[0])); i++)
241	{
242		#if VERBOSE > 1
243		output("Starting test for %s\n", scenar[i].descr);
244		#endif
245
246		/* Initialize the data structure */
247		ret = pthread_mutexattr_init(&ma);
248		if (ret != 0)  {  UNRESOLVED(ret, "Mutex attribute object init failed");  }
249
250		ret = pthread_mutexattr_settype(&ma, scenar[i].type);
251		if (ret != 0)  {  UNRESOLVED(ret, "Unable to set mutex type");  }
252
253		if ((pshared_ok > 0) && (scenar[i].pshared != 0))
254		{
255			ret = pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED);
256			if (ret != 0)  {  UNRESOLVED(ret, "Unable to set mutex process-shared");  }
257		}
258
259		ret = pthread_condattr_init(&ca);
260		if (ret != 0)  {  UNRESOLVED(ret, "Cond attribute object init failed");  }
261
262		if ((pshared_ok > 0) && (scenar[i].pshared != 0))
263		{
264			ret = pthread_condattr_setpshared(&ca, PTHREAD_PROCESS_SHARED);
265			if (ret != 0)  {  UNRESOLVED(ret, "Unable to set cond process-shared");  }
266		}
267
268		#ifdef USE_ALTCLK
269		if ((altclk_ok > 0) && (scenar[i].altclk != 0))
270		{
271			ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC);
272			if (ret != 0)  {  UNRESOLVED(ret, "Unable to set alternative (monotonic) clock for cond");  }
273		}
274		#endif
275
276		ret = pthread_mutex_init(&(data.mtx1), &ma);
277		if (ret != 0)  {  UNRESOLVED(ret, "Unable to init mutex 1");  }
278
279		ret = pthread_mutex_init(&(data.mtx2), &ma);
280		if (ret != 0)  {  UNRESOLVED(ret, "Unable to init mutex 2");  }
281
282		ret = pthread_cond_init(&(data.cnd), &ca);
283		if (ret != 0)  {  UNRESOLVED(ret, "Unable to initialize condvar");  }
284
285		data.boolcnd = 0;
286
287		ret = pthread_mutexattr_gettype(&ma, &(data.type));
288		if (ret != 0)  {  UNRESOLVED(ret, "Unable to get type from mutex attr");  }
289
290		#ifdef USE_ALTCLK
291		ret = pthread_condattr_getclock(&ca, &(data.cid));
292		if (ret != 0)  {  UNRESOLVED(ret, "Unable to get clock ID from cond attr");  }
293		#else
294		data.cid = CLOCK_REALTIME;
295		#endif
296
297		data.started = 0;
298		data.stopped = 0;
299
300		/* Start the threads */
301		#if VERBOSE > 1
302		output("Initialization OK, starting threads\n");
303		#endif
304		for (j = 0; j < NTHREADS; j++)
305		{
306			ret = pthread_create(&th[j], NULL, threaded, (void *)(long)(j & 1));
307			if (ret != 0)  {  UNRESOLVED(ret, "Thread creation failed");  }
308		}
309
310		/* Wait for the threads to be started */
311		do {
312			ret = pthread_mutex_lock(&(data.mtx1));
313			if (ret != 0)  {  UNRESOLVED(ret, "Unable to lock m1 in parent");  }
314			loc_started = data.started;
315			ret = pthread_mutex_unlock(&(data.mtx1));
316			if (ret != 0)  {  UNRESOLVED(ret, "Unable to unlock m1 in parent");  }
317		} while (loc_started < NTHREADS);
318
319		/* Broadcast the condition until all threads are terminated */
320		data.boolcnd = 1;
321		do {
322			ret = pthread_cond_broadcast(&(data.cnd));
323			if (ret != 0)  {  UNRESOLVED(ret, "Unable to broadcast cnd");  }
324			sched_yield();
325			ret = pthread_mutex_lock(&(data.mtx2));
326			if (ret != 0)  {  UNRESOLVED(ret, "Unable to lock m2 in parent");  }
327			loc_stopped = data.stopped;
328			ret = pthread_mutex_unlock(&(data.mtx2));
329			if (ret != 0)  {  UNRESOLVED(ret, "Unable to unlock m2 in parent");  }
330		} while (loc_stopped < NTHREADS);
331
332		/* Join the threads */
333		for (j = 0; j < NTHREADS; j++)
334		{
335			ret = pthread_join(th[j], NULL);
336			if (ret != 0)  {  UNRESOLVED(ret, "Thread join failed");  }
337		}
338
339		#if VERBOSE > 1
340		output("Test passed for %s\n", scenar[i].descr);
341		#endif
342
343		/* Destroy data */
344		ret = pthread_cond_destroy(&(data.cnd));
345		if (ret != 0)  {  UNRESOLVED(ret, "Cond destroy failed");  }
346
347		ret = pthread_mutex_destroy(&(data.mtx1));
348		if (ret != 0)  {  UNRESOLVED(ret, "Mutex 1 destroy failed");  }
349
350		ret = pthread_mutex_destroy(&(data.mtx2));
351		if (ret != 0)  {  UNRESOLVED(ret, "Mutex 2 destroy failed");  }
352
353		ret = pthread_condattr_destroy(&ca);
354		if (ret != 0)  {  UNRESOLVED(ret, "Cond attribute destroy failed");  }
355
356		ret = pthread_mutexattr_destroy(&ma);
357		if (ret != 0)  {  UNRESOLVED(ret, "Mutex attr destroy failed");  }
358	} /* Proceed to next case */
359
360
361	PASSED;
362}
363
364#else /* WITHOUT_XOPEN */
365int main(int argc, char * argv[])
366{
367	output_init();
368	UNTESTED("This test requires XSI features");
369}
370#endif
371
372