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