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 * This file is a stress test for the function pthread_exit.
18 *
19 * It aims to check that:
20 *  -> when the threads are joinable, pthread_join always retrieve the
21 *     correct value.
22 *  -> pthread_exit() frees all the resources used by the threads.
23 *
24 * The second assertion is implicitly checked by monitoring the system
25 * while the stress test is running.
26 *
27 */
28
29 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
30#define _POSIX_C_SOURCE 200112L
31
32 /* We need the XSI extention for some routines */
33#ifndef WITHOUT_XOPEN
34#define _XOPEN_SOURCE	600
35#endif
36/********************************************************************************************/
37/****************************** standard includes *****************************************/
38/********************************************************************************************/
39#include <pthread.h>
40#include <stdarg.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <unistd.h>
44
45#include <errno.h>
46#include <signal.h>
47#include <semaphore.h>
48
49/********************************************************************************************/
50/******************************   Test framework   *****************************************/
51/********************************************************************************************/
52#include "testfrmw.h"
53#include "testfrmw.c"
54 /* This header is responsible for defining the following macros:
55  * UNRESOLVED(ret, descr);
56  *    where descr is a description of the error and ret is an int (error code for example)
57  * FAILED(descr);
58  *    where descr is a short text saying why the test has failed.
59  * PASSED();
60  *    No parameter.
61  *
62  * Both three macros shall terminate the calling process.
63  * The testcase shall not terminate in any other maneer.
64  *
65  * The other file defines the functions
66  * void output_init()
67  * void output(char * string, ...)
68  *
69  * Those may be used to output information.
70  */
71
72/********************************************************************************************/
73/********************************** Configuration ******************************************/
74/********************************************************************************************/
75#ifndef SCALABILITY_FACTOR
76#define SCALABILITY_FACTOR 1
77#endif
78#ifndef VERBOSE
79#define VERBOSE 1
80#endif
81
82#define FACTOR 5
83
84/* This testcase needs the XSI features */
85#ifndef WITHOUT_XOPEN
86/********************************************************************************************/
87/***********************************    Test case   *****************************************/
88/********************************************************************************************/
89
90#include "threads_scenarii.c"
91
92/* This file will define the following objects:
93 * scenarii: array of struct __scenario type.
94 * NSCENAR : macro giving the total # of scenarii
95 * scenar_init(): function to call before use the scenarii array.
96 * scenar_fini(): function to call after end of use of the scenarii array.
97 */
98
99/********************************************************************************************/
100/***********************************    Real Test   *****************************************/
101/********************************************************************************************/
102
103char do_it = 1;
104long long iterations = 0;
105
106/* Handler for user request to terminate */
107void sighdl(int sig)
108{
109	/* do_it = 0 */
110	do {
111		do_it = 0;
112	}
113	while (do_it);
114}
115
116/* Cleanup handler to make sure the thread is exiting */
117void cleanup(void *arg)
118{
119	int ret = 0;
120	sem_t *sem = (sem_t *) arg;
121
122	/* Signal we're done (especially in case of a detached thread) */
123	do {
124		ret = sem_post(sem);
125	}
126	while ((ret == -1) && (errno == EINTR));
127	if (ret == -1) {
128		UNRESOLVED(errno, "Failed to wait for the semaphore");
129	}
130}
131
132/* Thread routine */
133void *threaded(void *arg)
134{
135	pthread_cleanup_push(cleanup, &scenarii[sc].sem);
136
137	pthread_exit(arg);
138	FAILED("the pthread_exit routine returned");
139
140	pthread_cleanup_pop(1);
141
142	return NULL;		/* For the sake of compiler */
143}
144
145/* main routine */
146int main(int argc, char *argv[])
147{
148	int ret, i;
149	void *rval;
150	struct sigaction sa;
151
152	pthread_t threads[NSCENAR * SCALABILITY_FACTOR * FACTOR];
153	int rets[NSCENAR * SCALABILITY_FACTOR * FACTOR];
154
155	/* Initialize output */
156	output_init();
157
158	/* Initialize scenarii table */
159	scenar_init();
160
161	/* Register the signal handler for SIGUSR1 */
162	sigemptyset(&sa.sa_mask);
163	sa.sa_flags = 0;
164	sa.sa_handler = sighdl;
165	if ((ret = sigaction(SIGUSR1, &sa, NULL))) {
166		UNRESOLVED(ret, "Unable to register signal handler");
167	}
168	if ((ret = sigaction(SIGALRM, &sa, NULL))) {
169		UNRESOLVED(ret, "Unable to register signal handler");
170	}
171#if VERBOSE > 1
172	output("[parent] Signal handler registered\n");
173#endif
174
175	while (do_it) {
176		/* Create all the threads */
177		for (i = 0; i < SCALABILITY_FACTOR * FACTOR; i++) {
178			for (sc = 0; sc < NSCENAR; sc++) {
179				/* Skip the alternative stack threads */
180				if (scenarii[sc].altstack != 0)
181					continue;
182
183				rets[i * NSCENAR + sc] =
184				    pthread_create(&threads[i * NSCENAR + sc],
185						   &scenarii[sc].ta, threaded,
186						   &threads[i * NSCENAR + sc]);
187				switch (scenarii[sc].result) {
188				case 0:	/* Operation was expected to succeed */
189					if (rets[i * NSCENAR + sc] != 0) {
190						UNRESOLVED(rets
191							   [i * NSCENAR + sc],
192							   "Failed to create this thread");
193					}
194					break;
195
196				case 1:	/* Operation was expected to fail */
197					if (rets[i * NSCENAR + sc] == 0) {
198						UNRESOLVED(-1,
199							   "An error was expected but the thread creation succeeded");
200					}
201					break;
202
203				case 2:	/* We did not know the expected result */
204				default:
205#if VERBOSE > 5
206					if (rets[i * NSCENAR + sc] == 0) {
207						output
208						    ("Thread has been created successfully for this scenario\n");
209					} else {
210						output
211						    ("Thread creation failed with the error: %s\n",
212						     strerror(rets
213							      [i * NSCENAR +
214							       sc]));
215					}
216#endif
217					;
218				}
219				if (rets[i * NSCENAR + sc] == 0) {
220					/* Just wait for the thread to terminate */
221					do {
222						ret =
223						    sem_wait(&scenarii[sc].sem);
224					}
225					while ((ret == -1) && (errno == EINTR));
226					if (ret == -1) {
227						UNRESOLVED(errno,
228							   "Failed to wait for the semaphore");
229					}
230				}
231			}
232		}
233
234		/* Join all the joinable threads and check the value */
235		for (i = 0; i < SCALABILITY_FACTOR * FACTOR; i++) {
236			for (sc = 0; sc < NSCENAR; sc++) {
237				if ((scenarii[sc].altstack == 0)
238				    && (scenarii[sc].detached == 0)
239				    && (rets[i * NSCENAR + sc] == 0)) {
240					ret =
241					    pthread_join(threads
242							 [i * NSCENAR + sc],
243							 &rval);
244					if (ret != 0) {
245						UNRESOLVED(ret,
246							   "Unable to join a thread");
247					}
248
249					if (rval !=
250					    (void *)&threads[i * NSCENAR +
251							     sc]) {
252						output
253						    ("arg: %p -- got %p -- NULL=%p\n",
254						     &threads[i * NSCENAR + sc],
255						     rval, NULL);
256						FAILED
257						    ("The retrieved error value is corrupted");
258					}
259				}
260			}
261		}
262
263		iterations++;
264	}
265
266	/* Destroy scenarii attributes */
267	scenar_fini();
268
269	/* Test passed */
270	output("pthread_exit stress test PASSED -- %llu iterations\n",
271	       iterations);
272	PASSED;
273}
274
275#else /* WITHOUT_XOPEN */
276int main(int argc, char *argv[])
277{
278	output_init();
279	UNTESTED("This test requires XSI features");
280}
281#endif
282