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_mutex_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 mutex
24  *      -> time = 0
25  *      -> init the 10 mutex with different attributes
26  *      -> output time
27  * -> When memory is full; undo everything:
28  *      -> time=0
29  *      -> destroy the 10 mutexes
30  *      -> output time
31  *      -> free memory
32  * -> We could additionally lock each mutex after init, and unlock before destroy.
33  */
34 
35 /********************************************************************************************/
36 /****************************** standard includes *****************************************/
37 /********************************************************************************************/
38 #include <pthread.h>
39 #include <errno.h>
40 #include <unistd.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <stdarg.h>
44 #include <sys/resource.h>
45 #include <sys/time.h>
46 
47 /********************************************************************************************/
48 /******************************   Test framework   *****************************************/
49 /********************************************************************************************/
50 #include "testfrmw.h"
51 #include "testfrmw.c"
52  /* This header is responsible for defining the following macros:
53   * UNRESOLVED(ret, descr);
54   *    where descr is a description of the error and ret is an int (error code for example)
55   * FAILED(descr);
56   *    where descr is a short text saying why the test has failed.
57   * PASSED();
58   *    No parameter.
59   *
60   * Both three macros shall terminate the calling process.
61   * The testcase shall not terminate in any other maneer.
62   *
63   * The other file defines the functions
64   * void output_init()
65   * void output(char * string, ...)
66   *
67   * Those may be used to output information.
68   */
69 
70 /********************************************************************************************/
71 /********************************** Configuration ******************************************/
72 /********************************************************************************************/
73 #ifndef SCALABILITY_FACTOR
74 #define SCALABILITY_FACTOR 1
75 #endif
76 #ifndef VERBOSE
77 #define VERBOSE 1
78 #endif
79 
80 #define WITH_LOCKS
81 
82 /********************************************************************************************/
83 /***********************************    Test case   *****************************************/
84 /********************************************************************************************/
85 
86 typedef struct _teststruct {
87 	pthread_mutex_t mtx[10 * SCALABILITY_FACTOR];
88 	pthread_mutexattr_t ma[5];
89 	pthread_mutexattr_t *pma[10 * SCALABILITY_FACTOR];
90 	struct _teststruct *prev;
91 } teststruct_t;
92 
93 int types[] = { PTHREAD_MUTEX_NORMAL,
94 	PTHREAD_MUTEX_ERRORCHECK,
95 	PTHREAD_MUTEX_RECURSIVE,
96 	PTHREAD_MUTEX_DEFAULT
97 };
98 
main(int argc,char * argv[])99 int main(int argc, char *argv[])
100 {
101 	struct rlimit rl;
102 	int ret;
103 	int i;
104 	teststruct_t *cur, *prev;
105 	struct timeval time_zero, time_cour, time_res, time_sav[8];
106 	long sav = 0;
107 
108 	/* Limit the process memory to a small value (64Mb for example). */
109 	rl.rlim_max = 1024 * 1024 * 32 * SCALABILITY_FACTOR;
110 	rl.rlim_cur = rl.rlim_max;
111 	if ((ret = setrlimit(RLIMIT_AS, &rl))) {
112 		UNRESOLVED(ret, "Memory limitation failed");
113 	}
114 #if VERBOSE > 1
115 	output(";Memory is now limited to %dMb\n", rl.rlim_max >> 20);
116 #endif
117 
118 	prev = NULL;
119 	cur = NULL;
120 
121 	/* Loop while we have memory left */
122 	while (1) {
123 		/* Allocate memory for 10 mutex and related stuff */
124 		cur = malloc(sizeof(teststruct_t));
125 		if (cur == NULL)	/* No memory left */
126 			break;
127 
128 		/* Link to the previous so we are able to free memory */
129 		cur->prev = prev;
130 		prev = cur;
131 
132 		/* Initialize the mutex attributes */
133 		/* We will have:
134 		 * pma[0] = NULL
135 		 * pma[1] = NORMAL type mutex attribute
136 		 * pma[2] = RECURSIVE type mutex attribute
137 		 * pma[3] = ERRORCHECK type mutex attribute
138 		 * pma[4] = DEFAULT type mutex attribute
139 		 * pma[5] = default mutex attribute
140 		 * pma[6] = NORMAL type mutex attribute
141 		 * pma[7] = RECURSIVE type mutex attribute
142 		 * pma[8] = ERRORCHECK type mutex attribute
143 		 * pma[9] = DEFAULT type mutex attribute
144 		 * pma[10] = pma[5] ...
145 		 */
146 		for (i = 0; i < 5; i++) {
147 			if ((ret = pthread_mutexattr_init(&(cur->ma[i])))) {
148 				UNRESOLVED(ret, "Mutex attribute init failed");
149 			}
150 			if (i) {
151 				if ((ret =
152 				     pthread_mutexattr_settype(&(cur->ma[i]),
153 							       types[i - 1]))) {
154 					UNRESOLVED(ret, "Mutex settype failed");
155 				}
156 			}
157 		}
158 		cur->pma[0] = NULL;
159 		for (i = 1; i < (10 * SCALABILITY_FACTOR); i++) {
160 			cur->pma[i] = &(cur->ma[i % 5]);
161 		}		/* The mutex attributes are now initialized */
162 
163 		/* Save the time */
164 		gettimeofday(&time_zero, NULL);
165 
166 		/* For each mutex, we will:
167 		 * - init the mutex
168 		 * - destroy the mutex
169 		 * - init the mutex
170 		 * - lock the mutex
171 		 * - unlock the mutex
172 		 * if WITH_LOCKS,
173 		 * - lock the mutex
174 		 */
175 		for (i = 0; i < 10 * SCALABILITY_FACTOR; i++) {
176 			ret = pthread_mutex_init(&(cur->mtx[i]), cur->pma[i]);
177 			if (ret) {
178 				UNRESOLVED(ret, "Mutex 1st init failed");
179 			}
180 			ret = pthread_mutex_destroy(&(cur->mtx[i]));
181 			if (ret) {
182 				UNRESOLVED(ret, "Mutex 1st destroy failed");
183 			}
184 			ret = pthread_mutex_init(&(cur->mtx[i]), cur->pma[i]);
185 			if (ret) {
186 				UNRESOLVED(ret, "Mutex 2nd init failed");
187 			}
188 			ret = pthread_mutex_lock(&(cur->mtx[i]));
189 			if (ret) {
190 				UNRESOLVED(ret, "Mutex 1st lock failed");
191 			}
192 			ret = pthread_mutex_unlock(&(cur->mtx[i]));
193 			if (ret) {
194 				UNRESOLVED(ret, "Mutex 1st unlock failed");
195 			}
196 #ifdef WITH_LOCKS
197 			ret = pthread_mutex_lock(&(cur->mtx[i]));
198 			if (ret) {
199 				UNRESOLVED(ret, "Mutex 2st lock failed");
200 			}
201 #endif
202 		}
203 		/* Compute the operation duration */
204 		gettimeofday(&time_cour, NULL);
205 		time_res.tv_usec =
206 		    time_cour.tv_usec + 1000000 - time_zero.tv_usec;
207 		if (time_res.tv_usec < 1000000) {
208 			time_res.tv_sec =
209 			    time_cour.tv_sec - 1 - time_zero.tv_sec;
210 		} else {
211 			time_res.tv_sec = time_cour.tv_sec - time_zero.tv_sec;
212 			time_res.tv_usec -= 1000000;
213 		}
214 
215 		if (sav > 3) {
216 			time_sav[4].tv_sec = time_sav[5].tv_sec;
217 			time_sav[4].tv_usec = time_sav[5].tv_usec;
218 			time_sav[5].tv_sec = time_sav[6].tv_sec;
219 			time_sav[5].tv_usec = time_sav[6].tv_usec;
220 			time_sav[6].tv_sec = time_sav[7].tv_sec;
221 			time_sav[6].tv_usec = time_sav[7].tv_usec;
222 			time_sav[7].tv_sec = time_res.tv_sec;
223 			time_sav[7].tv_usec = time_res.tv_usec;
224 		} else {
225 			time_sav[sav].tv_sec = time_res.tv_sec;
226 			time_sav[sav].tv_usec = time_res.tv_usec;
227 		}
228 		sav++;
229 #if VERBOSE > 2
230 		output("%4i.%06i;\n", time_res.tv_sec, time_res.tv_usec);
231 #endif
232 	}
233 	if (errno != ENOMEM) {
234 		UNRESOLVED(errno, "Memory not full");
235 	}
236 
237 	/* Now we just have to cleanup everything. */
238 	while (prev != NULL) {
239 		cur = prev;
240 		prev = cur->prev;
241 
242 		/* Free the mutex resources in the cur element */
243 		for (i = 0; i < 10 * SCALABILITY_FACTOR; i++) {
244 #ifdef WITH_LOCKS
245 			ret = pthread_mutex_unlock(&(cur->mtx[i]));
246 			if (ret) {
247 				UNRESOLVED(ret, "Mutex 2nd unlock failed");
248 			}
249 #endif
250 			ret = pthread_mutex_destroy(&(cur->mtx[i]));
251 			if (ret) {
252 				UNRESOLVED(ret, "Mutex 2nd destroy failed");
253 			}
254 		}
255 		/* Free the mutex attributes resources in the cur element */
256 		for (i = 0; i < 5; i++) {
257 			if ((ret = pthread_mutexattr_destroy(&(cur->ma[i])))) {
258 				UNRESOLVED(ret,
259 					   "Mutex 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 */
main(int argc,char * argv[])294 int main(int argc, char *argv[])
295 {
296 	output_init();
297 	UNRESOLVED(0, "This test requires XSI features");
298 }
299 #endif
300