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.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 *
17
18 * This file is a scalability test for the pthread_cond_init function.
19
20 * The steps are:
21 * -> Restrict the memory to 32Mb * SCALABILITY_FACTOR
22 * -> While there is free memory
23 *      -> allocate memory for 10 cond vars
24 *      -> time = 0
25 *      -> init the 10 cond vars with different attributes
26 *      -> output time
27 * -> When memory is full; undo everything:
28 *      -> time=0
29 *      -> destroy the 10 cond vars
30 *      -> output time
31 *      -> free memory
32 */
33
34 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
35#define _POSIX_C_SOURCE 200112L
36
37 /* We need XSI conformance for memory limitation */
38#ifndef WITHOUT_XOPEN
39#define _XOPEN_SOURCE	600
40
41/********************************************************************************************/
42/****************************** standard includes *****************************************/
43/********************************************************************************************/
44#include <pthread.h>
45#include <errno.h>
46#include <unistd.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <stdarg.h>
50#include <sys/resource.h>
51#include <sys/time.h>
52
53/********************************************************************************************/
54/******************************   Test framework   *****************************************/
55/********************************************************************************************/
56#include "testfrmw.h"
57#include "testfrmw.c"
58 /* This header is responsible for defining the following macros:
59  * UNRESOLVED(ret, descr);
60  *    where descr is a description of the error and ret is an int (error code for example)
61  * FAILED(descr);
62  *    where descr is a short text saying why the test has failed.
63  * PASSED();
64  *    No parameter.
65  *
66  * Both three macros shall terminate the calling process.
67  * The testcase shall not terminate in any other maneer.
68  *
69  * The other file defines the functions
70  * void output_init()
71  * void output(char * string, ...)
72  *
73  * Those may be used to output information.
74  */
75
76/********************************************************************************************/
77/********************************** Configuration ******************************************/
78/********************************************************************************************/
79#ifndef SCALABILITY_FACTOR
80#define SCALABILITY_FACTOR 1
81#endif
82#ifndef VERBOSE
83#define VERBOSE 1
84#endif
85
86/********************************************************************************************/
87/***********************************    Test case   *****************************************/
88/********************************************************************************************/
89
90typedef struct _teststruct {
91	pthread_cond_t cnd[10 * SCALABILITY_FACTOR];
92	pthread_condattr_t ca[4];
93	pthread_condattr_t *pca[10 * SCALABILITY_FACTOR];
94	struct _teststruct *prev;
95} teststruct_t;
96
97int main(int argc, char *argv[])
98{
99	struct rlimit rl;
100	int ret;
101	int i;
102	teststruct_t *cur, *prev;
103	struct timeval time_zero, time_cour, time_res, time_sav[8];
104	long sav = 0;
105
106	long monotonic, pshared;
107
108	pshared = sysconf(_SC_THREAD_PROCESS_SHARED);
109	monotonic = sysconf(_SC_MONOTONIC_CLOCK);
110#if VERBOSE > 1
111	output("Support for process-shared condvars: %li\n", pshared);
112	output("Support for monotonic clock        : %li\n", monotonic);
113#endif
114
115	/* Limit the process memory to a small value (32Mb for example). */
116	rl.rlim_max = 1024 * 1024 * 32 * SCALABILITY_FACTOR;
117	rl.rlim_cur = rl.rlim_max;
118	if ((ret = setrlimit(RLIMIT_AS, &rl))) {
119		UNRESOLVED(ret, "Memory limitation failed");
120	}
121#if VERBOSE > 1
122	output(";Memory is now limited to %dMb\n", rl.rlim_max >> 20);
123#endif
124
125	prev = NULL;
126	cur = NULL;
127
128	/* Loop while we have memory left */
129	while (1) {
130		/* Allocate memory for 10 mutex and related stuff */
131		cur = malloc(sizeof(teststruct_t));
132		if (cur == NULL)	/* No memory left */
133			break;
134
135		/* Link to the previous so we are able to free memory */
136		cur->prev = prev;
137		prev = cur;
138
139		/* Initialize the mutex attributes */
140		/* We will have:
141		 * pca[0] = NULL
142		 * pca[1] = Default cond attribute
143		 * pca[2] = (if supported) pshared cond attribute
144		 * pca[3] = (if supported) monotonic clock cond attribute
145		 * pca[4] = (if supported) pshared + monotonic
146		 * pca[5] = pca[0]...
147		 */
148		for (i = 0; i < 4; i++) {
149			ret = pthread_condattr_init(&(cur->ca[i]));
150			if (ret != 0) {
151				UNRESOLVED(ret, "Cond attribute init failed");
152			}
153
154			if ((monotonic > 0) && ((i == 2) || (i == 3))) {
155				ret =
156				    pthread_condattr_setclock(&(cur->ca[i]),
157							      CLOCK_MONOTONIC);
158				if (ret != 0) {
159					UNRESOLVED(ret,
160						   "Set monotonic clock failed");
161				}
162			}
163			if ((pshared > 0) && ((i == 1) || (i == 3))) {
164				ret =
165				    pthread_condattr_setpshared(&(cur->ca[i]),
166								PTHREAD_PROCESS_SHARED);
167				if (ret != 0) {
168					UNRESOLVED(ret,
169						   "Set process shared attribute failed");
170				}
171			}
172
173		}
174
175		for (i = 0; i < (10 * SCALABILITY_FACTOR); i++) {
176			cur->pca[i] = (i % 5) ? &(cur->ca[i % 4]) : NULL;
177		}		/* The mutex attributes are now initialized */
178
179		/* Save the time */
180		gettimeofday(&time_zero, NULL);
181
182		/* For each condvar, we will:
183		 * - init the condvar
184		 * - destroy the condvar
185		 * - init the condvar
186		 * - destroy the condvar
187		 * - init the condvar
188		 */
189		for (i = 0; i < 10 * SCALABILITY_FACTOR; i++) {
190			ret = pthread_cond_init(&(cur->cnd[i]), cur->pca[i]);
191			if (ret) {
192				UNRESOLVED(ret, "Cond 1st init failed");
193			}
194			ret = pthread_cond_destroy(&(cur->cnd[i]));
195			if (ret) {
196				UNRESOLVED(ret, "Cond 1st destroy failed");
197			}
198			ret = pthread_cond_init(&(cur->cnd[i]), cur->pca[i]);
199			if (ret) {
200				UNRESOLVED(ret, "Cond 2nd init failed");
201			}
202			ret = pthread_cond_destroy(&(cur->cnd[i]));
203			if (ret) {
204				UNRESOLVED(ret, "Cond 2nd destroy failed");
205			}
206			ret = pthread_cond_init(&(cur->cnd[i]), cur->pca[i]);
207			if (ret) {
208				UNRESOLVED(ret, "Cond 3rd init failed");
209			}
210		}
211		/* Compute the operation duration */
212		gettimeofday(&time_cour, NULL);
213		time_res.tv_usec =
214		    time_cour.tv_usec + 1000000 - time_zero.tv_usec;
215		if (time_res.tv_usec < 1000000) {
216			time_res.tv_sec =
217			    time_cour.tv_sec - 1 - time_zero.tv_sec;
218		} else {
219			time_res.tv_sec = time_cour.tv_sec - time_zero.tv_sec;
220			time_res.tv_usec -= 1000000;
221		}
222
223		if (sav > 3) {
224			time_sav[4].tv_sec = time_sav[5].tv_sec;
225			time_sav[4].tv_usec = time_sav[5].tv_usec;
226			time_sav[5].tv_sec = time_sav[6].tv_sec;
227			time_sav[5].tv_usec = time_sav[6].tv_usec;
228			time_sav[6].tv_sec = time_sav[7].tv_sec;
229			time_sav[6].tv_usec = time_sav[7].tv_usec;
230			time_sav[7].tv_sec = time_res.tv_sec;
231			time_sav[7].tv_usec = time_res.tv_usec;
232		} else {
233			time_sav[sav].tv_sec = time_res.tv_sec;
234			time_sav[sav].tv_usec = time_res.tv_usec;
235		}
236		sav++;
237	}
238	if (errno != ENOMEM) {
239		UNRESOLVED(errno, "Memory not full");
240	}
241
242	/* Now we just have to cleanup everything. */
243	while (prev != NULL) {
244		cur = prev;
245		prev = cur->prev;
246
247		/* Free the condvar resources in the cur element */
248		for (i = 0; i < 10 * SCALABILITY_FACTOR; i++) {
249			ret = pthread_cond_destroy(&(cur->cnd[i]));
250			if (ret) {
251				UNRESOLVED(ret, "Cond final destroy failed");
252			}
253		}
254		/* Free the cond attributes resources in the cur element */
255		for (i = 0; i < 4; i++) {
256			ret = pthread_condattr_destroy(&(cur->ca[i]));
257			if (ret != 0) {
258				UNRESOLVED(ret,
259					   "Cond attribute destroy failed");
260			}
261		}
262		/* Free the element memory */
263		free(cur);
264	}
265#if VERBOSE > 0
266	if (sav < 8) {
267		output("Not enough iterations to build statistics\n");
268	} else {
269		output("Duration for the operations:\n");
270		output(" %8i : %2i.%06i s\n", 0, time_sav[0].tv_sec,
271		       time_sav[0].tv_usec);
272		output(" %8i : %2i.%06i s\n", 1, time_sav[1].tv_sec,
273		       time_sav[1].tv_usec);
274		output(" %8i : %2i.%06i s\n", 2, time_sav[2].tv_sec,
275		       time_sav[2].tv_usec);
276		output(" %8i : %2i.%06i s\n", 3, time_sav[3].tv_sec,
277		       time_sav[3].tv_usec);
278		output(" [...]\n");
279		output(" %8i : %2i.%06i s\n", sav - 3, time_sav[4].tv_sec,
280		       time_sav[4].tv_usec);
281		output(" %8i : %2i.%06i s\n", sav - 2, time_sav[5].tv_sec,
282		       time_sav[5].tv_usec);
283		output(" %8i : %2i.%06i s\n", sav - 1, time_sav[6].tv_sec,
284		       time_sav[6].tv_usec);
285		output(" %8i : %2i.%06i s\n", sav, time_sav[7].tv_sec,
286		       time_sav[7].tv_usec);
287	}
288#endif
289
290	PASSED;
291}
292
293#else /* WITHOUT_XOPEN */
294int main(int argc, char *argv[])
295{
296	output_init();
297	UNRESOLVED(0, "This test requires XSI features");
298}
299#endif
300