• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 sample test aims to check the following assertion:
18  *
19  * If the calling thread is the last thread in the process,
20  * the effect are as if an implicit call to exit(0) had been made.
21 
22  * The steps are:
23  *
24  * -> main creates a thread.
25  * -> this thread forks(). The new process contains only 1 thread.
26  * -> the thread in the new process calls pthread_exit(non-0 value).
27  * -> main process joins the child process and checks the behavior
28  *     is as if exit(0) had been called.
29  *     The checked items are:
30  *       -> the return value.
31  *       -> the atexit() routines have been called.
32   */
33 
34  /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
35 #define _POSIX_C_SOURCE 200112L
36 
37  /* Some routines are part of the XSI Extensions */
38 #ifndef WITHOUT_XOPEN
39 #define _XOPEN_SOURCE	600
40 #endif
41 
42 /********************************************************************************************/
43 /****************************** standard includes *****************************************/
44 /********************************************************************************************/
45 #include <pthread.h>
46 #include <stdarg.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 
52 #include <sched.h>
53 #include <semaphore.h>
54 #include <errno.h>
55 #include <assert.h>
56 #include <sys/wait.h>
57 #include <sys/mman.h>
58 
59 /********************************************************************************************/
60 /******************************   Test framework   *****************************************/
61 /********************************************************************************************/
62 #include "../testfrmw/testfrmw.h"
63 #include "../testfrmw/testfrmw.c"
64  /* This header is responsible for defining the following macros:
65   * UNRESOLVED(ret, descr);
66   *    where descr is a description of the error and ret is an int (error code for example)
67   * FAILED(descr);
68   *    where descr is a short text saying why the test has failed.
69   * PASSED();
70   *    No parameter.
71   *
72   * Both three macros shall terminate the calling process.
73   * The testcase shall not terminate in any other maneer.
74   *
75   * The other file defines the functions
76   * void output_init()
77   * void output(char * string, ...)
78   *
79   * Those may be used to output information.
80   */
81 
82 /********************************************************************************************/
83 /********************************** Configuration ******************************************/
84 /********************************************************************************************/
85 #ifndef VERBOSE
86 #define VERBOSE 1
87 #endif
88 
89 /********************************************************************************************/
90 /***********************************    Test cases  *****************************************/
91 /********************************************************************************************/
92 
93 #include "../testfrmw/threads_scenarii.c"
94 
95 /* This file will define the following objects:
96  * scenarii: array of struct __scenario type.
97  * NSCENAR : macro giving the total # of scenarii
98  * scenar_init(): function to call before use the scenarii array.
99  * scenar_fini(): function to call after end of use of the scenarii array.
100  */
101 
102 /********************************************************************************************/
103 /***********************************    Real Test   *****************************************/
104 /********************************************************************************************/
105 
106 /* This will be used to control that atexit() has been called */
107 int *ctl;
108 long mf;
109 
clnp(void)110 void clnp(void)
111 {
112 	*ctl = 1;
113 }
114 
115 /* Thread routine */
threaded(void * arg)116 void *threaded(void *arg)
117 {
118 	int ret = 0;
119 
120 	pid_t pid, chk;
121 	int status;
122 
123 	if (mf > 0)
124 		*ctl = 0;
125 
126 #if VERBOSE > 0
127 	fflush(stdout);
128 #endif
129 
130 	pid = fork();
131 	if (pid == (pid_t) - 1) {
132 		UNRESOLVED(errno, "Failed to fork()");
133 	}
134 	if (pid == 0) {
135 		/* children */
136 		if (mf > 0) {
137 			ret = atexit(clnp);
138 			if (ret != 0) {
139 				UNRESOLVED(ret,
140 					   "Failed to register atexit function");
141 			}
142 		}
143 
144 		/* exit the last (and only) thread */
145 		pthread_exit(&ret);
146 
147 		FAILED
148 		    ("pthread_exit() did not terminate the process when there was only 1 thread");
149 	}
150 
151 	/* Only the parent process goes this far */
152 	chk = waitpid(pid, &status, 0);
153 	if (chk != pid) {
154 		output("Expected pid: %i. Got %i\n", (int)pid, (int)chk);
155 		UNRESOLVED(errno, "Waitpid failed");
156 	}
157 
158 	if (WIFSIGNALED(status)) {
159 		output("Child process killed with signal %d\n",
160 		       WTERMSIG(status));
161 		UNRESOLVED(-1, "Child process was killed");
162 	}
163 
164 	if (WIFEXITED(status)) {
165 		ret = WEXITSTATUS(status);
166 	} else {
167 		UNRESOLVED(-1, "Child process was neither killed nor exited");
168 	}
169 
170 	if (ret != 0) {
171 		output("Exit status was: %i\n", ret);
172 		FAILED("The child process did not exit with 0 status.");
173 	}
174 
175 	if (mf > 0)
176 		if (*ctl != 1)
177 			FAILED
178 			    ("pthread_exit() in the last thread did not execute atexit() routines");
179 
180 	/* Signal we're done (especially in case of a detached thread) */
181 	do {
182 		ret = sem_post(&scenarii[sc].sem);
183 	}
184 	while ((ret == -1) && (errno == EINTR));
185 	if (ret == -1) {
186 		UNRESOLVED(errno, "Failed to wait for the semaphore");
187 	}
188 
189 	return NULL;
190 }
191 
192 /* Main routine */
main(void)193 int main(void)
194 {
195 	int ret = 0;
196 	pthread_t child;
197 
198 	mf = sysconf(_SC_MAPPED_FILES);
199 
200 	output_init();
201 
202 	scenar_init();
203 
204 	/* We want to share some memory with the child process */
205 	if (mf > 0) {
206 		/* We will place the test data in a mmaped file */
207 		char filename[] = "/tmp/pthread_exit_6-1-XXXXXX";
208 		size_t sz;
209 		void *mmaped;
210 		int fd;
211 		char *tmp;
212 
213 		/* We now create the temp files */
214 		fd = mkstemp(filename);
215 		if (fd == -1) {
216 			UNRESOLVED(errno,
217 				   "Temporary file could not be created");
218 		}
219 
220 		/* and make sure the file will be deleted when closed */
221 		unlink(filename);
222 
223 #if VERBOSE > 1
224 		output("Temp file created (%s).\n", filename);
225 #endif
226 
227 		sz = (size_t) sysconf(_SC_PAGESIZE);
228 
229 		tmp = calloc(1, sz);
230 		if (tmp == NULL) {
231 			UNRESOLVED(errno, "Memory allocation failed");
232 		}
233 
234 		/* Write the data to the file.  */
235 		if (write(fd, tmp, sz) != (ssize_t) sz) {
236 			UNRESOLVED(sz, "Writting to the file failed");
237 		}
238 
239 		free(tmp);
240 
241 		/* Now we can map the file in memory */
242 		mmaped =
243 		    mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
244 		if (mmaped == MAP_FAILED) {
245 			UNRESOLVED(errno, "mmap failed");
246 		}
247 
248 		ctl = (int *)mmaped;
249 
250 		/* Our datatest structure is now in shared memory */
251 #if VERBOSE > 1
252 		output("Testdata allocated in shared memory.\n");
253 #endif
254 	}
255 
256 	for (sc = 0; sc < NSCENAR; sc++) {
257 #if VERBOSE > 0
258 		output("-----\n");
259 		output("Starting test with scenario (%i): %s\n", sc,
260 		       scenarii[sc].descr);
261 #endif
262 
263 		ret = pthread_create(&child, &scenarii[sc].ta, threaded, &ctl);
264 		switch (scenarii[sc].result) {
265 		case 0:	/* Operation was expected to succeed */
266 			if (ret != 0) {
267 				UNRESOLVED(ret, "Failed to create this thread");
268 			}
269 			break;
270 
271 		case 1:	/* Operation was expected to fail */
272 			if (ret == 0) {
273 				UNRESOLVED(-1,
274 					   "An error was expected but the thread creation succeeded");
275 			}
276 			break;
277 
278 		case 2:	/* We did not know the expected result */
279 		default:
280 #if VERBOSE > 0
281 			if (ret == 0) {
282 				output
283 				    ("Thread has been created successfully for this scenario\n");
284 			} else {
285 				output
286 				    ("Thread creation failed with the error: %s\n",
287 				     strerror(ret));
288 			}
289 #endif
290 		}
291 		if (ret == 0) {	/* The new thread is running */
292 			if (scenarii[sc].detached == 0) {
293 				ret = pthread_join(child, NULL);
294 				if (ret != 0) {
295 					UNRESOLVED(ret,
296 						   "Unable to join a thread");
297 				}
298 			} else {
299 				/* Just wait for the thread to terminate */
300 				do {
301 					ret = sem_wait(&scenarii[sc].sem);
302 				}
303 				while ((ret == -1) && (errno == EINTR));
304 				if (ret == -1) {
305 					UNRESOLVED(errno,
306 						   "Failed to wait for the semaphore");
307 				}
308 			}
309 		}
310 	}
311 
312 	scenar_fini();
313 #if VERBOSE > 0
314 	output("-----\n");
315 	output("All test data destroyed\n");
316 	output("Test PASSED\n");
317 #endif
318 
319 	PASSED;
320 }
321