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 sample test aims to check the following assertion:
20 * The function fails and returns ENOMEM if there is not enough memory.
21
22
23
24 * The steps are:
25 * * Fork
26 * * New process sets its memory resource limit to a minimum value, then
27 *  -> Allocate all the available memory
28 *  -> call pthread_cond_init()
29 *  -> free the memory
30 *  -> Checks that pthread_cond_init() returned 0 or ENOMEM.
31 * * Parent process waits for the child.
32 */
33
34 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
35 #define _POSIX_C_SOURCE 200112L
36
37 /* We need the setrlimit() function from X/OPEN standard */
38 #ifndef WITHOUT_XOPEN
39 #define _XOPEN_SOURCE	600
40
41/********************************************************************************************/
42/****************************** standard includes *****************************************/
43/********************************************************************************************/
44 #include <pthread.h>
45 #include <stdio.h>
46 #include <unistd.h>
47 #include <stdlib.h>
48 #include <errno.h>
49 #include <signal.h>
50 #include <sys/wait.h>
51 #include <sys/resource.h>
52 #include <stdarg.h>
53
54/********************************************************************************************/
55/******************************   Test framework   *****************************************/
56/********************************************************************************************/
57 #include "testfrmw.h"
58 #include "testfrmw.c"
59 /* This header is responsible for defining the following macros:
60  * UNRESOLVED(ret, descr);
61  *    where descr is a description of the error and ret is an int (error code for example)
62  * FAILED(descr);
63  *    where descr is a short text saying why the test has failed.
64  * PASSED();
65  *    No parameter.
66  *
67  * Both three macros shall terminate the calling process.
68  * The testcase shall not terminate in any other maneer.
69  *
70  * The other file defines the functions
71  * void output_init()
72  * void output(char * string, ...)
73  *
74  * Those may be used to output information.
75  */
76
77/********************************************************************************************/
78/********************************** Configuration ******************************************/
79/********************************************************************************************/
80#ifndef VERBOSE
81#define VERBOSE 1
82#endif
83
84/********************************************************************************************/
85/***********************************    Test case   *****************************************/
86/********************************************************************************************/
87
88int main(int argc, char * argv[])
89{
90	pid_t child;
91
92
93	pthread_cond_t  cnd;
94	pthread_condattr_t ca[4];
95	pthread_condattr_t *pca[5];
96
97	int ret=0;
98	int i;
99	int retini[5] = {-1,-1,-1,-1,-1};
100	int retdtr[5]= {-1,-1,-1,-1,-1};
101
102	void * ptr, *ptr_prev=NULL;
103
104	int sz = 0;
105	struct rlimit rl;
106
107	int status=0;
108
109
110	output_init();
111
112	child = fork();
113
114	if (child == (pid_t)-1)
115	{ UNRESOLVED(errno, "Fork failed"); }
116
117	if (child != 0) /* We are the father */
118	{
119		if (child != waitpid(child, &status, 0))
120		{  UNRESOLVED(errno, "Waitpid failed"); }
121
122		if (WIFSIGNALED(status))
123		{ UNRESOLVED(WTERMSIG(status),
124			"The child process was killed."); }
125
126		if (WIFEXITED(status))
127			return WEXITSTATUS(status);
128
129		UNRESOLVED(0, "Child process neither returned nor was killed.");
130	}
131
132	/* Only the child goes further */
133
134	/* We initialize the different cond attributes */
135	for (i=0; (i<4) && (ret == 0); i++)
136	{
137		pca[i] = &ca[i];
138		ret = pthread_condattr_init(pca[i]);
139	}
140	if (ret)
141	{ UNRESOLVED(ret, "Cond attribute init failed"); }
142	pca[4] = (pthread_condattr_t *) NULL;
143
144	ret = pthread_condattr_setpshared(pca[0], PTHREAD_PROCESS_SHARED);
145	if (ret != 0) {  UNRESOLVED(ret, "Cond attribute PSHARED failed");  }
146	ret = pthread_condattr_setpshared(pca[1], PTHREAD_PROCESS_SHARED);
147	if (ret != 0) {  UNRESOLVED(ret, "Cond attribute PSHARED failed");  }
148
149	if (sysconf(_SC_MONOTONIC_CLOCK) > 0)
150	{
151		ret = pthread_condattr_setclock(pca[1], CLOCK_MONOTONIC);
152		if (ret != 0) {  UNRESOLVED(ret, "Cond set monotonic clock failed");  }
153		ret = pthread_condattr_setclock(pca[2], CLOCK_MONOTONIC);
154		if (ret != 0) {  UNRESOLVED(ret, "Cond set monotonic clock failed");  }
155	}
156
157	sz = sysconf(_SC_PAGESIZE);
158
159
160	/* Limit the process memory to a small value (64Mb for example). */
161	rl.rlim_max=1024*1024*64;
162	rl.rlim_cur=1024*1024*64;
163	if ((ret = setrlimit(RLIMIT_AS,  &rl)))
164	{ UNRESOLVED(ret, "Memory limitation failed"); }
165
166
167	#if VERBOSE > 1
168	output("Ready to take over memory. Page size is %d\n", sz);
169	#endif
170
171	/* Allocate all available memory */
172	while (1)
173	{
174		ptr = malloc( sz ); /* Allocate one page of memory */
175		if (ptr == NULL)
176			break;
177		#if VERBOSE > 1
178		ret++;
179		#endif
180		*(void **)ptr = ptr_prev; /* Write into the allocated page */
181		ptr_prev = ptr;
182	}
183	#if VERBOSE > 1
184	output("%d pages were allocated before failure\n", ret);
185	ret = 0;
186	#endif
187
188	while (1)
189	{
190		ptr = malloc( sizeof(void*) ); /* Allocate every remaining bits of memory */
191		if (ptr == NULL)
192			break;
193		#if VERBOSE > 1
194		ret++;
195		#endif
196		*(void **)ptr = ptr_prev; /* Keep track of allocated memory */
197		ptr_prev = ptr;
198	}
199	#if VERBOSE > 1
200	output("%d additional spaces were allocated before failure\n", ret);
201	ret = 0;
202	#endif
203	if (errno != ENOMEM)
204		UNRESOLVED(errno, "Memory not full");
205
206	/* Now that memory is full, we try to initialize a cond */
207	for (i=0; i<5; i++)
208	{
209		retini[i] = pthread_cond_init(&cnd, pca[i]);
210		if (!retini[i]) /* If cond has been initialized, we destroy it */
211			retdtr[i] = pthread_cond_destroy(&cnd);
212	}
213
214	/* We can now free the memory */
215	while (ptr_prev != NULL)
216	{
217		ptr = ptr_prev;
218		ptr_prev = *(void **)ptr;
219		free(ptr);
220	}
221
222	#if VERBOSE > 1
223	output("Memory is released\n");
224	#endif
225
226	for (i=0; i<4; i++)
227		pthread_condattr_destroy(pca[i]);
228
229
230	for (i=0; i<5; i++)
231	{
232		if (retini[i] != 0 && retini[i] !=ENOMEM)
233		{  FAILED("Cond init returned a wrong error code when no memory was left"); }
234
235		if (retini[i] == 0)
236		{
237			#if VERBOSE > 0
238			output("Cond (%i) initialization succeeded when memory is full\n", i);
239			#endif
240			if (retdtr[i] != 0)
241			{  UNRESOLVED( retdtr[i],  "Cond destroy failed for a cond inilialized under full memory"); }
242		}
243		#if VERBOSE > 0
244		else
245		{
246			output("Cond (%i) initialization failed with ENOMEM\n", i);
247		}
248		#endif
249	}
250	PASSED;
251}
252
253#else /* WITHOUT_XOPEN */
254int main(int argc, char * argv[])
255{
256	output_init();
257	UNTESTED("This test requires XSI features");
258}
259#endif
260