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., 59
15 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
16 *
17
18
19 * This file is a stress test for the pthread_mutex_lock function.
20
21 * The steps are:
22 * -> For each king of mutex, we create 10*F threads (F is a scalability factor)
23 * -> we call those threads 1 to 10.
24 *    -> thread 1 sends signal USR2 to the other 9 threads (which have a handler for it)
25 *    -> thread 2 to 6 are loops
26 *          {
27 *               mutex_lock
28 *               if (ctrl) exit
29 *               ctrl = 1
30 *               yield
31 *               ctrl= 0
32 *               mutex unlock
33 *          }
34 *     -> thread 7 & 8 have a timedlock instead of lock
35 *     -> thread 9 & 10 have a trylock instead of lock
36 *
37 * -> the whole process stop when receiving signal SIGUSR1.
38 *      This goal is achieved with a "do_it" variable.
39 *
40 * NOTE: With gcc/linux, the flag "-lrt" must be specified at link time.
41 */
42
43 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
44 #define _POSIX_C_SOURCE 200112L
45
46 /* We enable the following line to have mutex attributes defined */
47#ifndef WITHOUT_XOPEN
48 #define _XOPEN_SOURCE	600
49#endif
50
51/********************************************************************************************/
52/****************************** standard includes *****************************************/
53/********************************************************************************************/
54 #include <pthread.h>
55 #include <errno.h>
56 #include <semaphore.h>
57 #include <signal.h>
58 #include <unistd.h>
59#if _POSIX_TIMEOUTS < 0
60#error "This sample needs POSIX TIMEOUTS option support"
61#endif
62#if _POSIX_TIMEOUTS == 0
63#warning "This sample needs POSIX TIMEOUTS option support"
64#endif
65#if _POSIX_TIMERS < 0
66#error "This sample needs POSIX TIMERS option support"
67#endif
68#if _POSIX_TIMERS == 0
69#warning "This sample needs POSIX TIMERS option support"
70#endif
71
72
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <stdarg.h>
76 #include <time.h> /* required for the pthread_mutex_timedlock() function */
77
78/********************************************************************************************/
79/******************************   Test framework   *****************************************/
80/********************************************************************************************/
81 #include "testfrmw.h"
82 #include "testfrmw.c"
83 /* This header is responsible for defining the following macros:
84  * UNRESOLVED(ret, descr);
85  *    where descr is a description of the error and ret is an int (error code for example)
86  * FAILED(descr);
87  *    where descr is a short text saying why the test has failed.
88  * PASSED();
89  *    No parameter.
90  *
91  * Both three macros shall terminate the calling process.
92  * The testcase shall not terminate in any other maneer.
93  *
94  * The other file defines the functions
95  * void output_init()
96  * void output(char * string, ...)
97  *
98  * Those may be used to output information.
99  */
100
101/********************************************************************************************/
102/********************************** Configuration ******************************************/
103/********************************************************************************************/
104#ifndef SCALABILITY_FACTOR
105#define SCALABILITY_FACTOR 1
106#endif
107#ifndef VERBOSE
108#define VERBOSE 2
109#endif
110#define N 2  /* N * 10 * 6 * SCALABILITY_FACTOR threads will be created */
111
112/********************************************************************************************/
113/***********************************    Test case   *****************************************/
114/********************************************************************************************/
115char do_it=1;
116#ifndef WITHOUT_XOPEN
117int types[]={PTHREAD_MUTEX_NORMAL,
118					PTHREAD_MUTEX_ERRORCHECK,
119					PTHREAD_MUTEX_RECURSIVE,
120					PTHREAD_MUTEX_DEFAULT};
121#endif
122
123/* The following type represents the data
124 * for one group of ten threads */
125typedef struct
126{
127	pthread_t  threads[10]; /* The 10 threads */
128	pthread_mutex_t mtx;  /* The mutex those threads work on */
129	char ctrl;                       /* The value used to check the behavior */
130	char sigok;                   /* Used to tell the threads they can return */
131	sem_t  semsig;             /* Semaphore for synchronizing the signal handler */
132	int id;                             /* An identifier for the threads group */
133	int tcnt;	 /* we need to make sure the threads are started before killing 'em */
134	pthread_mutex_t tmtx;
135	unsigned long long sigcnt, opcnt; /* We count every iteration */
136} cell_t;
137
138pthread_key_t  _c; /* this key will always contain a pointer to the thread's cell */
139
140/***** The next function is in charge of sending signal USR2 to
141 * all the other threads in its cell, until the end of the test. */
142void * sigthr(void * arg)
143{
144	int ret;
145	int i=0;
146	cell_t * c = (cell_t *)arg;
147
148	do
149	{
150		sched_yield();
151		ret = pthread_mutex_lock(&(c->tmtx));
152		if (ret != 0)  {  UNRESOLVED(ret, "Failed to lock the mutex");  }
153		i = c->tcnt;
154		ret = pthread_mutex_unlock(&(c->tmtx));
155		if (ret != 0)  {  UNRESOLVED(ret, "Failed to unlock the mutex");  }
156	} while (i<9);
157
158	/* Until we must stop, do */
159	while (do_it)
160	{
161		/* Wait for the semaphore */
162		ret = sem_wait(&(c->semsig));
163		if (ret != 0)
164		{  UNRESOLVED(errno, "Sem wait failed in signal thread"); }
165
166		/* Kill the next thread */
167		i %= 9;
168		ret = pthread_kill(c->threads[++i], SIGUSR2);
169		if (ret != 0)
170		{  UNRESOLVED(ret, "Thread kill failed in signal thread");  }
171
172		/* Increment the signal counter */
173		c->sigcnt++;
174	}
175
176	/* Tell the other threads they can now stop */
177	do {  c->sigok = 1;  }
178	while ( c->sigok == 0 );
179
180	return NULL;
181}
182
183/***** The next function is the signal handler
184 * for all the others threads in the cell */
185void sighdl(int sig)
186{
187	int ret;
188	cell_t * c = (cell_t *) pthread_getspecific(_c);
189	ret = sem_post(&(c->semsig));
190	if (ret != 0)
191	{  UNRESOLVED(errno, "Unable to post semaphore in signal handler");  }
192}
193
194/***** The next function can return only when the sigthr has terminated.
195 * This avoids the signal thread try to kill a terminated thread. */
196void waitsigend(cell_t * c)
197{
198	while ( c->sigok == 0 )
199	{  sched_yield();  }
200}
201
202/***** The next function aims to control that no other thread
203 * owns the mutex at the same time */
204void control(cell_t * c, char * loc)
205{
206	*loc++; /* change the local control value */
207	if (c->ctrl != 0)
208	{  FAILED("Got a non-zero value - two threads owns the mutex");  }
209	c->ctrl = *loc;
210	sched_yield();
211	if (c->ctrl != *loc)
212	{  FAILED("Got a different value - another thread touched protected data");  }
213	c->ctrl = 0;
214
215	/* Avoid some values for the next control */
216	if (*loc == 120)
217		*loc = -120;
218	if (*loc == -1)
219		*loc = 1;
220}
221
222/***** The next 3 functions are the worker threads
223 */
224void * lockthr(void * arg)
225{
226	int ret;
227	char loc; /* Local value for control */
228	cell_t * c = (cell_t *)arg;
229
230	/* Set the thread local data key value (used in the signal handler) */
231	ret = pthread_setspecific(_c, arg);
232	if (ret != 0)
233	{  UNRESOLVED(ret, "Unable to assign the thread-local-data key");  }
234
235	/* Signal we're started */
236	ret = pthread_mutex_lock(&(c->tmtx));
237	if (ret != 0)  {  UNRESOLVED(ret, "Failed to lock the mutex");  }
238	c->tcnt += 1;
239	ret = pthread_mutex_unlock(&(c->tmtx));
240	if (ret != 0)  {  UNRESOLVED(ret, "Failed to unlock the mutex");  }
241
242	do
243	{
244		/* Lock, control, then unlock */
245		ret = pthread_mutex_lock(&(c->mtx));
246		if (ret != 0)
247		{  UNRESOLVED(ret, "Mutex lock failed in worker thread");  }
248
249		control(c, &loc);
250
251		ret = pthread_mutex_unlock(&(c->mtx));
252		if (ret != 0)
253		{  UNRESOLVED(ret, "Mutex unlock failed in worker thread");  }
254
255		/* Increment the operation counter */
256		c->opcnt++;
257	}
258	while (do_it);
259
260	/* Wait for the signal thread to terminate before we can exit */
261	waitsigend(c);
262	return NULL;
263}
264
265void * timedlockthr(void * arg)
266{
267	int ret;
268	char loc; /* Local value for control */
269	struct timespec ts;
270	cell_t * c = (cell_t *)arg;
271
272	/* Set the thread local data key value (used in the signal handler) */
273	ret = pthread_setspecific(_c, arg);
274	if (ret != 0)
275	{  UNRESOLVED(ret, "Unable to assign the thread-local-data key");  }
276
277	/* Signal we're started */
278	ret = pthread_mutex_lock(&(c->tmtx));
279	if (ret != 0)  {  UNRESOLVED(ret, "Failed to lock the mutex");  }
280	c->tcnt += 1;
281	ret = pthread_mutex_unlock(&(c->tmtx));
282	if (ret != 0)  {  UNRESOLVED(ret, "Failed to unlock the mutex");  }
283
284	do
285	{
286		/* Lock, control, then unlock */
287		do
288		{
289			ret = clock_gettime(CLOCK_REALTIME, &ts);
290			if (ret != 0)
291			{  UNRESOLVED(errno, "Unable to get time for timeout");  }
292			ts.tv_sec++; /* We will wait for 1 second */
293			ret = pthread_mutex_timedlock(&(c->mtx), &ts);
294		} while (ret == ETIMEDOUT);
295		if (ret != 0)
296		{  UNRESOLVED(ret, "Timed mutex lock failed in worker thread");  }
297
298		control(c, &loc);
299
300		ret = pthread_mutex_unlock(&(c->mtx));
301		if (ret != 0)
302		{  UNRESOLVED(ret, "Mutex unlock failed in worker thread");  }
303
304		/* Increment the operation counter */
305		c->opcnt++;
306	}
307	while (do_it);
308
309	/* Wait for the signal thread to terminate before we can exit */
310	waitsigend(c);
311	return NULL;
312}
313
314void * trylockthr(void * arg)
315{
316	int ret;
317	char loc; /* Local value for control */
318	cell_t * c = (cell_t *)arg;
319
320	/* Set the thread local data key value (used in the signal handler) */
321	ret = pthread_setspecific(_c, arg);
322	if (ret != 0)
323	{  UNRESOLVED(ret, "Unable to assign the thread-local-data key");  }
324
325	/* Signal we're started */
326	ret = pthread_mutex_lock(&(c->tmtx));
327	if (ret != 0)  {  UNRESOLVED(ret, "Failed to lock the mutex");  }
328	c->tcnt += 1;
329	ret = pthread_mutex_unlock(&(c->tmtx));
330	if (ret != 0)  {  UNRESOLVED(ret, "Failed to unlock the mutex");  }
331
332	do
333	{
334		/* Lock, control, then unlock */
335		do
336		{
337			ret = pthread_mutex_trylock(&(c->mtx));
338		}  while (ret == EBUSY);
339		if (ret != 0)
340		{  UNRESOLVED(ret, "Mutex lock try failed in worker thread");  }
341
342		control(c, &loc);
343
344		ret = pthread_mutex_unlock(&(c->mtx));
345		if (ret != 0)
346		{  UNRESOLVED(ret, "Mutex unlock failed in worker thread");  }
347
348		/* Increment the operation counter */
349		c->opcnt++;
350	}
351	while (do_it);
352
353	/* Wait for the signal thread to terminate before we can exit */
354	waitsigend(c);
355	return NULL;
356}
357
358/***** The next function initializes a cell_t object
359 * This includes running the threads */
360void cell_init(int id, cell_t * c, pthread_mutexattr_t *pma)
361{
362	int ret, i;
363	pthread_attr_t  pa; /* We will specify a minimal stack size */
364
365	/* mark this group with its ID */
366	c->id = id;
367	/* Initialize some other values */
368	c->sigok = 0;
369	c->ctrl = 0;
370	c->sigcnt = 0;
371	c->opcnt = 0;
372	c->tcnt = 0;
373
374	/* Initialize the mutex */
375	ret = pthread_mutex_init(&(c->tmtx), NULL);
376	if (ret != 0)
377	{  UNRESOLVED(ret, "Mutex init failed"); }
378	ret = pthread_mutex_init(&(c->mtx), pma);
379	if (ret != 0)
380	{  UNRESOLVED(ret, "Mutex init failed"); }
381	#if VERBOSE > 1
382	output("Mutex initialized in cell %i\n", id);
383	#endif
384
385	/* Initialize the semaphore */
386	ret = sem_init(&(c->semsig), 0, 0);
387	if (ret != 0)
388	{  UNRESOLVED(errno, "Sem init failed"); }
389	#if VERBOSE > 1
390	output("Semaphore initialized in cell %i\n", id);
391	#endif
392
393	/* Create the thread attribute with the minimal size */
394	ret = pthread_attr_init(&pa);
395	if (ret != 0)
396	{  UNRESOLVED(ret, "Unable to create pthread attribute object");  }
397	ret = pthread_attr_setstacksize(&pa, sysconf(_SC_THREAD_STACK_MIN));
398	if (ret != 0)
399 	{  UNRESOLVED(ret, "Unable to specify minimal stack size");  }
400
401
402	/* Create the signal thread */
403	ret = pthread_create(&(c->threads[0]), &pa, sigthr, (void *) c);
404	if (ret != 0)
405	{  UNRESOLVED(ret, "Unable to create the signal thread"); }
406
407	/* Create 5 "lock" threads */
408	for (i=1; i<=5; i++)
409	{
410		ret = pthread_create(&(c->threads[i]), &pa, lockthr, (void *) c);
411		if (ret != 0)
412		{  UNRESOLVED(ret, "Unable to create a locker thread"); }
413	}
414
415	/* Create 2 "timedlock" threads */
416	for (i=6; i<=7; i++)
417	{
418		ret = pthread_create(&(c->threads[i]), &pa, timedlockthr, (void *) c);
419		if (ret != 0)
420		{  UNRESOLVED(ret, "Unable to create a (timed) locker thread"); }
421	}
422
423	/* Create 2 "trylock" threads */
424	for (i=8; i<=9; i++)
425	{
426		ret = pthread_create(&(c->threads[i]), &pa, trylockthr, (void *) c);
427		if (ret != 0)
428		{  UNRESOLVED(ret, "Unable to create a (try) locker thread"); }
429	}
430
431	#if VERBOSE > 1
432	output("All threads initialized in cell %i\n", id);
433	#endif
434
435	/* Destroy the thread attribute object */
436	ret = pthread_attr_destroy(&pa);
437	if (ret != 0)
438	{  UNRESOLVED(ret, "Unable to destroy thread attribute object");  }
439
440	/* Tell the signal thread to start working */
441	ret = sem_post(&(c->semsig));
442	if (ret != 0)
443	{  UNRESOLVED(ret, "Unable to post signal semaphore");  }
444}
445
446/***** The next function destroys a cell_t object
447 * This includes stopping the threads */
448void cell_fini(
449				int id,
450				cell_t * c,
451				unsigned long long * globalopcount,
452				unsigned long long * globalsigcount  )
453{
454	int ret, i;
455
456	/* Just a basic check */
457	if (id != c->id)
458	{
459		output("Something is wrong: Cell %i has id %i\n", id, c->id);
460		FAILED("Some memory has been corrupted");
461	}
462
463	/* Start with joining the threads */
464	for (i=0; i<10; i++)
465	{
466		ret = pthread_join(c->threads[i], NULL);
467		if (ret != 0)
468		{  UNRESOLVED(ret, "Unable to join a thread");  }
469	}
470
471	/* Destroy the semaphore and the mutex */
472	ret = sem_destroy(&(c->semsig));
473	if (ret != 0)
474	{  UNRESOLVED(errno, "Unable to destroy the semaphore");  }
475
476	ret = pthread_mutex_destroy(&(c->mtx));
477	if (ret != 0)
478	{
479		output("Unable to destroy the mutex in cell %i (ret = %i)\n", id, ret);
480		FAILED("Mutex destruction failed");
481	}
482
483	/* Report the cell counters */
484	*globalopcount += c->opcnt;
485	*globalsigcount += c->sigcnt;
486	#if VERBOSE > 1
487	output("Counters for cell %i:\n\t%llu locks and unlocks\n\t%llu signals\n",
488	                   id,
489	                   c->opcnt,
490	                   c->sigcnt);
491	#endif
492
493	/* We are done with this cell. */
494}
495
496
497/**** Next function is called when the process is killed with SIGUSR1
498 * It tells every threads in every cells to stop their work.
499 */
500void globalsig(int sig)
501{
502	output("Signal received, processing. Please wait...\n");
503	do { do_it=0; }
504	while (do_it);
505}
506
507
508/******
509 * Last but not least, the main function
510 */
511int main(int argc, char * argv[])
512{
513	/* Main is responsible for :
514	 * the mutex attributes initializing
515	 * the creation of the cells
516	 * the destruction of everything on SIGUSR1 reception
517	 */
518
519	int ret;
520	int i;
521	struct sigaction sa;
522	unsigned long long globopcnt=0, globsigcnt=0;
523
524	#ifndef WITHOUT_XOPEN
525	int sz = 2 + (sizeof(types) / sizeof(int));
526	#else
527	int sz = 2;
528	#endif
529	pthread_mutexattr_t ma[sz-1];
530	pthread_mutexattr_t *pma[sz];
531
532	cell_t data[sz * N * SCALABILITY_FACTOR];
533
534	pma[sz-1] = NULL;
535
536	#if VERBOSE > 0
537	output("Mutex lock / unlock stress sample is starting\n");
538	output("Kill with SIGUSR1 to stop the process\n");
539	output("\t kill -USR1 <pid>\n\n");
540	#endif
541
542	/* Initialize the mutex attributes */
543	for (i=0; i<sz-1; i++)
544	{
545		pma[i] = &ma[i];
546		ret = pthread_mutexattr_init(pma[i]);
547		if (ret != 0)
548		{  UNRESOLVED(ret, "Unable to init a mutex attribute object");  }
549		#ifndef WITHOUT_XOPEN  /* we have the mutex attribute types */
550		if (i != 0)
551		{
552			ret = pthread_mutexattr_settype(pma[i], types[i-1]);
553			if (ret != 0)
554			{
555				UNRESOLVED(ret, "Unable to set type of a mutex attribute object");
556			}
557		}
558		#endif
559	}
560	#if VERBOSE > 1
561	output("%i mutex attribute objects were initialized\n", sz - 1);
562	#endif
563
564	/* Initialize the thread-local-data key */
565	ret = pthread_key_create(&_c, NULL);
566	if (ret != 0)
567	{  UNRESOLVED(ret, "Unable to initialize TLD key");  }
568	#if VERBOSE > 1
569	output("TLD key initialized\n");
570	#endif
571
572	/* Register the signal handler for SIGUSR1  */
573	sigemptyset (&sa.sa_mask);
574	sa.sa_flags = 0;
575	sa.sa_handler = globalsig;
576	if ((ret = sigaction (SIGUSR1, &sa, NULL)))
577	{ UNRESOLVED(ret, "Unable to register signal handler"); }
578
579	/* Register the signal handler for SIGUSR2  */
580	sa.sa_handler = sighdl;
581	if ((ret = sigaction (SIGUSR2, &sa, NULL)))
582	{ UNRESOLVED(ret, "Unable to register signal handler"); }
583
584	/* Start every threads */
585	#if VERBOSE > 0
586	output("%i cells of 10 threads are being created...\n", sz * N * SCALABILITY_FACTOR);
587	#endif
588	for (i=0; i< sz * N * SCALABILITY_FACTOR; i++)
589		cell_init(i, &data[i], pma[i % sz]);
590	#if VERBOSE > 0
591	output("All threads created and running.\n");
592	#endif
593
594	/* We stay here while not interrupted */
595	do { sched_yield(); }
596	while (do_it);
597
598	#if VERBOSE > 0
599	output("Starting to join the threads...\n");
600	#endif
601	/* Everybody is stopping, we must join them, and destroy the cell data */
602	for (i=0; i< sz * N * SCALABILITY_FACTOR; i++)
603		cell_fini(i, &data[i], &globopcnt, &globsigcnt);
604
605	/* Destroy the mutex attributes objects */
606	for (i=0; i<sz-1; i++)
607	{
608		ret = pthread_mutexattr_destroy(pma[i]);
609		if (ret != 0)
610		{  UNRESOLVED(ret, "Unable to destroy a mutex attribute object");  }
611	}
612
613	/* Destroy the thread-local-data key */
614	ret = pthread_key_delete(_c);
615	if (ret != 0)
616	{  UNRESOLVED(ret, "Unable to destroy TLD key");  }
617	#if VERBOSE > 1
618	output("TLD key destroyed\n");
619	#endif
620
621	/* output the total counters */
622	#if VERBOSE > 1
623	output("===============================================\n");
624	#endif
625	#if VERBOSE > 0
626	output("Total counters:\n\t%llu locks and unlocks\n\t%llu signals\n",
627	                   globopcnt,
628	                   globsigcnt);
629	output("pthread_mutex_lock stress test passed.\n");
630	#endif
631
632	PASSED;
633}
634