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_lock function.
19  * The goal is to test if there is a limit on the number
20  *  of threads waiting on the same mutex.
21 
22  * The steps are:
23  * -> Create 5 mutex with different attributes.
24  * -> lock the 5 mutex in the main thread
25  * -> Create the maximum amount of threads allowed on the system.
26  * -> each thread, for each mutex:
27  *       - locks the mutex
28  *       - increments a counter
29  *       - unlocks the mutex
30  *       - if the counter equals the amount of threads,
31  */
32 
33 /********************************************************************************************/
34 /****************************** standard includes *****************************************/
35 /********************************************************************************************/
36 #include <pthread.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <stdarg.h>
42 
43 /********************************************************************************************/
44 /******************************   Test framework   *****************************************/
45 /********************************************************************************************/
46 #include "testfrmw.h"
47 #include "testfrmw.c"
48  /* This header is responsible for defining the following macros:
49   * UNRESOLVED(ret, descr);
50   *    where descr is a description of the error and ret is an int (error code for example)
51   * FAILED(descr);
52   *    where descr is a short text saying why the test has failed.
53   * PASSED();
54   *    No parameter.
55   *
56   * Both three macros shall terminate the calling process.
57   * The testcase shall not terminate in any other maneer.
58   *
59   * The other file defines the functions
60   * void output_init()
61   * void output(char * string, ...)
62   *
63   * Those may be used to output information.
64   */
65 
66 /********************************************************************************************/
67 /********************************** Configuration ******************************************/
68 /********************************************************************************************/
69 #ifndef SCALABILITY_FACTOR
70 #define SCALABILITY_FACTOR 1	/* This is not used in this testcase */
71 #endif
72 #ifndef VERBOSE
73 #define VERBOSE 2
74 #endif
75 
76 /********************************************************************************************/
77 /***********************************    Test case   *****************************************/
78 /********************************************************************************************/
79 
80 #ifndef WITHOUT_XOPEN
81 int types[] = {
82 	PTHREAD_MUTEX_NORMAL,
83 	PTHREAD_MUTEX_ERRORCHECK,
84 	PTHREAD_MUTEX_RECURSIVE,
85 	PTHREAD_MUTEX_DEFAULT
86 };
87 #endif
88 
89 /* The mutex the threads will block on */
90 pthread_mutex_t mtx[5];
91 
92 /* The condition used to signal the main thread to go to the next step */
93 pthread_cond_t cnd;
94 pthread_mutex_t m;
95 
96 /* The shared data used to control the results of the test */
97 unsigned long nbthOK[5];
98 unsigned long nbthNOK[5];
99 unsigned long nbthTOT;
100 
101 /*****
102  *
103  */
threaded(void * arg)104 void *threaded(void *arg)
105 {
106 	int ret;
107 	int i;
108 	int bool;
109 
110 	for (i = 0; i < 5; i++) {
111 		ret = pthread_mutex_lock(&mtx[i]);
112 		if (ret == 0) {	/* The thread was blocked successfuly */
113 			/* We increment nbth[i] */
114 			ret = pthread_mutex_lock(&m);
115 			if (ret != 0) {
116 				UNRESOLVED(ret, "Unable to lock 'm'");
117 			}
118 			nbthOK[i]++;
119 			bool = ((nbthOK[i] + nbthNOK[i]) >= nbthTOT);
120 			ret = pthread_mutex_unlock(&m);
121 			if (ret != 0) {
122 				UNRESOLVED(ret, "Unable to unlock 'm'");
123 			}
124 
125 			/* We can unlock the test mutex */
126 			ret = pthread_mutex_unlock(&mtx[i]);
127 			if (ret != 0) {
128 				FAILED("Unlocking a test mutex failed");
129 			}
130 		} else {	/* Locking the test mutex failed */
131 
132 			/* We increment nbth[i] */
133 			ret = pthread_mutex_lock(&m);
134 			if (ret != 0) {
135 				UNRESOLVED(ret, "Unable to lock 'm'");
136 			}
137 			nbthNOK[i]++;
138 			bool = ((nbthOK[i] + nbthNOK[i]) >= nbthTOT);
139 			ret = pthread_mutex_unlock(&m);
140 			if (ret != 0) {
141 				UNRESOLVED(ret, "Unable to unlock 'm'");
142 			}
143 		}
144 
145 		/* When every thread has passed the lock call, bool is true.
146 		   we signal the main thread to release the next mutex. */
147 
148 		if (bool) {
149 			ret = pthread_cond_signal(&cnd);
150 			if (ret != 0) {
151 				UNRESOLVED(ret,
152 					   "Signaling the condition failed");
153 			}
154 		}
155 	}
156 
157 	/* The test is terminated, the thread can die */
158 	ret = pthread_detach(pthread_self());
159 	if (ret != 0) {
160 		UNRESOLVED(ret, "Thread detach failed");
161 	}
162 
163 	return NULL;
164 }
165 
main(int argc,char * argv[])166 int main(int argc, char *argv[])
167 {
168 	pthread_t th;
169 	pthread_attr_t tha;
170 	pthread_mutexattr_t ma;
171 	int ret;
172 	int i;
173 
174 	output_init();
175 
176 #if VERBOSE > 1
177 	output("Test starting, initializing data\n");
178 #endif
179 
180 	/* Init the shared data */
181 	for (i = 0; i < 4; i++) {
182 		nbthOK[i] = 0;
183 		nbthNOK[i] = 0;
184 	}
185 	nbthTOT = 0;
186 
187 	/* Init the cnd */
188 	ret = pthread_mutex_init(&m, NULL);
189 	if (ret != 0) {
190 		UNRESOLVED(ret, "Unable to initialize 'm'");
191 	}
192 	ret = pthread_cond_init(&cnd, NULL);
193 	if (ret != 0) {
194 		UNRESOLVED(ret, "Unable to initialize 'cnd'");
195 	}
196 
197 	/* Init the 5 mutexes */
198 	ret = pthread_mutexattr_init(&ma);
199 	if (ret != 0) {
200 		UNRESOLVED(ret, "Unable to initialize 'ma'");
201 	}
202 
203 	for (i = 0; i < 5; i++) {
204 #ifndef WITHOUT_XOPEN
205 		if (i > 0) {
206 			ret = pthread_mutexattr_settype(&ma, types[i - 1]);
207 			if (ret != 0) {
208 				UNRESOLVED(ret,
209 					   "Unable to set mutex attribute type");
210 			}
211 		}
212 #endif
213 		ret = pthread_mutex_init(&mtx[i], &ma);
214 		if (ret != 0) {
215 			UNRESOLVED(ret, "A mutex init failed");
216 		}
217 	}
218 
219 	ret = pthread_mutexattr_destroy(&ma);
220 	if (ret != 0) {
221 		UNRESOLVED(ret, "Unable to destroy the mutex attribute object");
222 	}
223 
224 	/* Lock the mutexes */
225 	for (i = 0; i < 5; i++) {
226 		ret = pthread_mutex_lock(&mtx[i]);
227 		if (ret != 0) {
228 			UNRESOLVED(ret,
229 				   "Unable to lock a mutex for the first time");
230 		}
231 	}
232 
233 	/* Init the threads attribute */
234 	ret = pthread_attr_init(&tha);
235 	if (ret != 0) {
236 		UNRESOLVED(ret, "Thread attribute init failed");
237 	}
238 
239 	ret = pthread_attr_setstacksize(&tha, sysconf(_SC_THREAD_STACK_MIN));
240 	if (ret != 0) {
241 		UNRESOLVED(ret, "Unable to set stack size to minimum value");
242 	}
243 
244 	/* Create as many threads as possible */
245 #if VERBOSE > 1
246 	output("Creating threads...\n");
247 #endif
248 	do {
249 		ret = pthread_create(&th, &tha, threaded, NULL);
250 		if (ret == 0)
251 			nbthTOT++;
252 	} while (ret == 0);
253 
254 #if VERBOSE > 1
255 	output("Created %d threads.\n", nbthTOT);
256 #endif
257 
258 	/* lock m */
259 	ret = pthread_mutex_lock(&m);
260 	if (ret != 0) {
261 		UNRESOLVED(ret, "Unable to lock 'm' in main thread");
262 	}
263 
264 	/* For each mutex */
265 	for (i = 0; i < 5; i++) {
266 		/* Yield to let other threads enter the lock function */
267 		sched_yield();
268 
269 		/* unlock the test mutex */
270 		ret = pthread_mutex_unlock(&mtx[i]);
271 		if (ret != 0) {
272 			UNRESOLVED(ret,
273 				   "Unable to unlock a test mutex in main thread");
274 		}
275 
276 		/* wait for cnd */
277 		do {
278 			ret = pthread_cond_wait(&cnd, &m);
279 		}
280 		while ((ret == 0) && ((nbthOK[i] + nbthNOK[i]) < nbthTOT));
281 		if (ret != 0) {
282 			UNRESOLVED(ret, "Unable to wait for 'cnd'");
283 		}
284 	}
285 
286 	/* unlock m */
287 	ret = pthread_mutex_unlock(&m);
288 	if (ret != 0) {
289 		UNRESOLVED(ret, "Final 'm' unlock failed");
290 	}
291 
292 	/* Destroy everything */
293 	ret = pthread_attr_destroy(&tha);
294 	if (ret != 0) {
295 		UNRESOLVED(ret, "Final thread attribute destroy failed");
296 	}
297 
298 	for (i = 0; i < 5; i++) {
299 		ret = pthread_mutex_destroy(&mtx[i]);
300 		if (ret != 0) {
301 			UNRESOLVED(ret, "Unable to destroy a test mutex");
302 		}
303 	}
304 
305 	ret = pthread_cond_destroy(&cnd);
306 	if (ret != 0) {
307 		UNRESOLVED(ret, "Final cond destroy failed");
308 	}
309 
310 	ret = pthread_mutex_destroy(&m);
311 	if (ret != 0) {
312 		UNRESOLVED(ret, "Final mutex destroy failed");
313 	}
314 
315 	/* Output the results */
316 	output("Sample results:\n");
317 	output(" %lu threads were created\n", nbthTOT);
318 	for (i = 0; i < 5; i++) {
319 		output(" %lu threads have waited on mutex %i\n", nbthOK[i],
320 		       i + 1);
321 		output("  (and %lu threads could not wait)\n", nbthNOK[i]);
322 		ret += nbthNOK[i];
323 	}
324 
325 	/* Exit */
326 	if (ret == 0) {
327 		PASSED;
328 	} else {
329 		FAILED("There may be an issue in scalability");
330 	}
331 }
332