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 concurrent mutex having threads pending.
21
22 * The steps are:
23 * -> create some mutex attributes objects
24 * -> As long as nothing fails, do
25 *    - create a thread.
26 *       - this thread initializes a mutex with one of the mutex attributes
27 *       - lock this mutex
28 *       - create another thread which waits on the mutex (and hangs) then returns
29 *       - wait for a condition
30 *       - unlock the mutex.
31 *       - join the thread
32 * -> When a create operation fails, broadcast the condition then join every threads.
33 *
34 * Additional note:
35 *    This test will test only N/2 parallel mutex, where N is the max number of threads.
36 *    It would be possible to create N parallel mutex with a slightly different algorithme:
37 *     the main thread owns each mutex, then creates a thread which will block.
38 *    This test could be written too. The current algorithm will give more stress to
39 *     the mutex threads queues mechanism, as the threads are always different.
40 */
41
42 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
43#define _POSIX_C_SOURCE 200112L
44
45 /* We enable the following line to have mutex attributes defined */
46#ifndef WITHOUT_XOPEN
47#define _XOPEN_SOURCE	600
48#endif
49
50/********************************************************************************************/
51/****************************** standard includes *****************************************/
52/********************************************************************************************/
53#include <pthread.h>
54#include <errno.h>
55#include <unistd.h>
56#include <stdio.h>
57#include <stdlib.h>
58#include <stdarg.h>
59
60/********************************************************************************************/
61/******************************   Test framework   *****************************************/
62/********************************************************************************************/
63#include "testfrmw.h"
64#include "testfrmw.c"
65 /* This header is responsible for defining the following macros:
66  * UNRESOLVED(ret, descr);
67  *    where descr is a description of the error and ret is an int (error code for example)
68  * FAILED(descr);
69  *    where descr is a short text saying why the test has failed.
70  * PASSED();
71  *    No parameter.
72  *
73  * Both three macros shall terminate the calling process.
74  * The testcase shall not terminate in any other maneer.
75  *
76  * The other file defines the functions
77  * void output_init()
78  * void output(char * string, ...)
79  *
80  * Those may be used to output information.
81  */
82
83/********************************************************************************************/
84/********************************** Configuration ******************************************/
85/********************************************************************************************/
86#ifndef SCALABILITY_FACTOR
87#define SCALABILITY_FACTOR 1	/* This is not used in this testcase */
88#endif
89#ifndef VERBOSE
90#define VERBOSE 2
91#endif
92
93/********************************************************************************************/
94/***********************************    Test case   *****************************************/
95/********************************************************************************************/
96
97#ifndef WITHOUT_XOPEN
98int types[] = {
99	PTHREAD_MUTEX_NORMAL,
100	PTHREAD_MUTEX_ERRORCHECK,
101	PTHREAD_MUTEX_RECURSIVE,
102	PTHREAD_MUTEX_DEFAULT
103};
104#endif
105
106/* The condition used to signal the main thread to go to the next step */
107pthread_cond_t cnd;
108pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
109char do_it;
110unsigned long counter;
111
112/* Mutex attribute objects and pointers */
113pthread_mutexattr_t *pma[6];
114#ifdef WITHOUT_XOPEN
115pthread_mutexattr_t ma[1];
116#else
117pthread_mutexattr_t ma[5];
118#endif
119
120/* Test data type */
121typedef struct _td {
122	pthread_t child;
123	int id;
124	pthread_mutex_t mtx;
125	int error;
126	struct _td *next;	/* It is a chained list */
127} testdata_t;
128
129/* Thread attribute object */
130pthread_attr_t ta;
131
132/*****
133 * Level 2 - grandchild function
134 */
135void *sub(void *arg)
136{
137	testdata_t *td = (testdata_t *) arg;
138	td->error = pthread_mutex_lock(&(td->mtx));
139	if (td->error != 0) {
140		/* Print out the error */
141		output("PROBLEM: Unable to lock the mutex in thread %i\n",
142		       td->id);
143	} else {
144		td->error = pthread_mutex_unlock(&(td->mtx));
145		if (td->error != 0) {
146			UNRESOLVED(td->error,
147				   "Mutex unlock failed. Mutex data was corrupted?");
148		}
149	}
150
151	return NULL;
152}
153
154/*****
155 * Level 1 - child function
156 */
157void *threaded(void *arg)
158{
159	testdata_t *td = (testdata_t *) arg;
160	int ret;
161	int ret_create;
162	pthread_t ch;
163
164	ret = pthread_mutex_lock(&m);
165	if (ret != 0) {
166		UNRESOLVED(ret, "Unable to lock 'm' in child");
167	}
168	/* Mark this thread as started */
169	counter++;
170
171	/* Initialize the mutex with the mutex attribute */
172	ret = pthread_mutex_init(&(td->mtx), pma[td->id % 6]);
173	if (ret != 0) {
174		UNRESOLVED(ret, "Unable to initialize a mutex");
175	}
176
177	/* Lock the mutex */
178	td->error = pthread_mutex_lock(&(td->mtx));
179	if (td->error != 0) {
180		/* If the lock failed, we stop now */
181		ret = pthread_mutex_unlock(&m);
182		if (ret != 0) {
183			UNRESOLVED(ret, "Unable to unlock 'm' in child");
184		}
185		return NULL;
186	}
187
188	/* Create the child thread */
189	ret_create = pthread_create(&ch, &ta, sub, arg);
190
191	/* Wait for the condition */
192	while (do_it) {
193		ret = pthread_cond_wait(&cnd, &m);
194		if (ret != 0) {
195			UNRESOLVED(ret, "Unable to wait for condvar");
196		}
197	}
198	ret = pthread_mutex_unlock(&m);
199	if (ret != 0) {
200		UNRESOLVED(ret, "Unable to unlock 'm' in child");
201	}
202
203	/* Unlock the mutex and release the child */
204	ret = pthread_mutex_unlock(&(td->mtx));
205	if (ret != 0) {
206		UNRESOLVED(ret,
207			   "Mutex unlock failed. Mutex data was corrupted?");
208	}
209
210	/* If the child exists, join it now */
211	if (ret_create == 0) {
212		ret = pthread_join(ch, NULL);
213		if (ret != 0) {
214			UNRESOLVED(ret, "Grandchild join failed");
215		}
216	}
217
218	/* Destroy the test mutex */
219	ret = pthread_mutex_destroy(&(td->mtx));
220	if (ret != 0) {
221		UNRESOLVED(ret, "Test mutex destroy failed. Corrupted data?");
222	}
223
224	/* We're done */
225	return NULL;
226}
227
228/*****
229 * Level 0 - main function
230 */
231int main(int argc, char *argv[])
232{
233	int ret;
234	int i;
235	int errors;
236	testdata_t sentinel;
237	testdata_t *cur, *tmp;
238
239	output_init();
240
241#if VERBOSE > 1
242	output("Test starting, initializing data\n");
243#endif
244
245	do_it = 1;
246	errors = 0;
247	counter = 0;
248	sentinel.next = NULL;
249	sentinel.id = 0;
250	cur = &sentinel;
251
252	/* Initialize the 6 pma objects */
253	pma[0] = NULL;
254	pma[1] = &ma[0];
255	ret = pthread_mutexattr_init(pma[1]);
256	if (ret != 0) {
257		UNRESOLVED(ret, "Mutex attribute init failed");
258	}
259#ifdef WITHOUT_XOPEN
260	/* We only have default attributes objects */
261	pma[2] = pma[0];
262	pma[4] = pma[0];
263	pma[3] = pma[1];
264	pma[5] = pma[1];
265#if VERBOSE > 1
266	output("Default mutex attribute object was initialized\n");
267#endif
268#else
269	/* We can use the different mutex types */
270	for (i = 0; i < 4; i++) {
271		pma[i + 2] = &ma[i + 1];
272		ret = pthread_mutexattr_init(pma[i + 2]);
273		if (ret != 0) {
274			UNRESOLVED(ret, "Mutex attribute init failed");
275		}
276		ret = pthread_mutexattr_settype(pma[i + 2], types[i]);
277		if (ret != 0) {
278			UNRESOLVED(ret, "Mutex attribute settype failed");
279		}
280	}
281#if VERBOSE > 1
282	output("%d types of mutex attribute objects were initialized\n",
283	       sizeof(types) / sizeof(types[0]));
284#endif
285#endif
286
287	/* Initialize the thread attribute object */
288	ret = pthread_attr_init(&ta);
289	if (ret != 0) {
290		UNRESOLVED(ret, "Thread attribute init failed");
291	}
292	ret = pthread_attr_setstacksize(&ta, sysconf(_SC_THREAD_STACK_MIN));
293	if (ret != 0) {
294		UNRESOLVED(ret, "Unable to set stack size to minimum value");
295	}
296
297	/* Lock m */
298	ret = pthread_mutex_lock(&m);
299	if (ret != 0) {
300		UNRESOLVED(ret, "Unable to lock 'm' in main");
301	}
302#if VERBOSE > 1
303	output("Ready to create the threads, processing...\n");
304#endif
305
306	/* create the threads */
307	while (1) {
308		tmp = malloc(sizeof(testdata_t));
309		if (tmp == NULL) {
310			/* We cannot create anymore testdata */
311			break;
312		}
313
314		/* We have a new test data structure */
315		ret = pthread_create(&(tmp->child), &ta, threaded, tmp);
316		if (ret != 0) {
317			/* We cannot create more threads */
318			free((void *)tmp);
319			break;
320		}
321
322		cur->next = tmp;
323		tmp->id = cur->id + 1;
324		tmp->error = 0;
325		cur = tmp;
326
327		/* The new thread was created, let's start it */
328		do {
329			/* Unlock m so the thread can acquire it */
330			ret = pthread_mutex_unlock(&m);
331			if (ret != 0) {
332				UNRESOLVED(ret,
333					   "Unlock 'm' failed in main loop");
334			}
335			/* Make sure the thread has a chance to run */
336			sched_yield();
337			/* Get m back */
338			ret = pthread_mutex_lock(&m);
339			if (ret != 0) {
340				UNRESOLVED(ret, "Lock 'm' failed in main loop");
341			}
342			/* If the counter has been incremented, this means this child is in the cond wait loop */
343		} while (counter != cur->id);
344	}
345
346	/* Unable to create more threads, let's signal the cond and join the threads */
347#if VERBOSE > 1
348	if (tmp == NULL) {
349		output("Cannot malloc more memory for the test data.\n");
350	} else {
351		output("Cannot create another thread (error: %d).\n", ret);
352	}
353	output("The children will now be signaled.\n");
354#endif
355	do_it = 0;
356	ret = pthread_cond_broadcast(&cnd);
357	if (ret != 0) {
358		UNRESOLVED(ret, "Cond broadcast failed");
359	}
360
361	ret = pthread_mutex_unlock(&m);
362	if (ret != 0) {
363		UNRESOLVED(ret, "Unable to unlock m after broadcast");
364	}
365#if VERBOSE > 1
366	output("The children are terminating. We will join them.\n");
367#endif
368
369	/* All the threads are terminating, we can join the children and destroy the testdata */
370	cur = &sentinel;
371	while (cur->next != NULL) {
372		/* Remove the first item from the list */
373		tmp = cur->next;
374		cur->next = tmp->next;
375
376		/* Join the thread from the current item */
377		ret = pthread_join(tmp->child, NULL);
378		if (ret != 0) {
379			UNRESOLVED(ret, "Unable to join a child");
380		}
381
382		/* get the useful data */
383		if (tmp->error != 0)
384			errors++;
385
386		/* Free the memory */
387		free((void *)tmp);
388	}
389
390	/* We are done */
391
392	/* Exit */
393	if (errors == 0) {
394#if VERBOSE > 1
395		output("The test passed successfully.\n");
396		output("  %i mutex were created and locked.\n", counter);
397		output("  No error was encountered\n");
398#endif
399		PASSED;
400	} else {
401#if VERBOSE > 0
402		output("The test failed.\n");
403		output("  %i mutex were created.\n", counter);
404		output("  %i lock operation failed.\n", errors);
405#endif
406		FAILED("There may be an issue in scalability");
407	}
408}
409