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  * This sample test aims to check the following assertion:
19  * The function does not return an error code of EINTR
20 
21 
22  * The steps are:
23  *
24  * -> Create some threads which wait for a condition.
25  * -> Create a worker thread which broadcasts this condition.
26  * -> Another thread loops on killing the worker thread.
27  *
28  */
29 
30  /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
31  #define _POSIX_C_SOURCE 200112L
32 
33 
34  /********************************************************************************************/
35 /****************************** standard includes *****************************************/
36 /********************************************************************************************/
37  #include <pthread.h>
38  #include <stdarg.h>
39  #include <stdio.h>
40  #include <stdlib.h>
41  #include <unistd.h>
42 
43  #include <semaphore.h>
44  #include <errno.h>
45  #include <signal.h>
46  #include <time.h>
47 
48 /********************************************************************************************/
49 /******************************   Test framework   *****************************************/
50 /********************************************************************************************/
51  #include "testfrmw.h"
52  #include "testfrmw.c"
53  /* This header is responsible for defining the following macros:
54   * UNRESOLVED(ret, descr);
55   *    where descr is a description of the error and ret is an int (error code for example)
56   * FAILED(descr);
57   *    where descr is a short text saying why the test has failed.
58   * PASSED();
59   *    No parameter.
60   *
61   * Both three macros shall terminate the calling process.
62   * The testcase shall not terminate in any other maneer.
63   *
64   * The other file defines the functions
65   * void output_init()
66   * void output(char * string, ...)
67   *
68   * Those may be used to output information.
69   */
70 
71 /********************************************************************************************/
72 /********************************** Configuration ******************************************/
73 /********************************************************************************************/
74 #define WITH_SYNCHRO
75 #ifndef VERBOSE
76 #define VERBOSE 2
77 #endif
78 
79 /********************************************************************************************/
80 /***********************************    Test case   *****************************************/
81 /********************************************************************************************/
82 
83 char do_it=1;
84 char woken=0;
85 unsigned long count_cnd_sig=0, count_cnd_wup=0;
86 #ifdef WITH_SYNCHRO
87 sem_t semsig1;
88 sem_t semsig2;
89 unsigned long count_sig=0;
90 #endif
91 
92 sigset_t usersigs;
93 
94 typedef struct
95 {
96 	int	sig;
97 #ifdef WITH_SYNCHRO
98 	sem_t	*sem;
99 #endif
100 } thestruct;
101 
102 struct
103 {
104 	pthread_mutex_t mtx;
105 	pthread_cond_t cnd;
106 } data;
107 
108 /* the following function keeps on sending the signal to the process */
sendsig(void * arg)109 void * sendsig (void * arg)
110 {
111 	thestruct *thearg = (thestruct *) arg;
112 	int ret;
113 	pid_t process;
114 
115 	process=getpid();
116 
117 	/* We block the signals SIGUSR1 and SIGUSR2 for this THREAD */
118 	ret = pthread_sigmask(SIG_BLOCK, &usersigs, NULL);
119 	if (ret != 0)  {  UNRESOLVED(ret, "Unable to block SIGUSR1 and SIGUSR2 in signal thread");  }
120 
121 	while (do_it)
122 	{
123 		#ifdef WITH_SYNCHRO
124 		if ((ret = sem_wait(thearg->sem)))
125 		{ UNRESOLVED(errno, "Sem_wait in sendsig"); }
126 		count_sig++;
127 		#endif
128 
129 		ret = kill(process, thearg->sig);
130 		if (ret != 0)  { UNRESOLVED(errno, "Kill in sendsig"); }
131 
132 	}
133 
134 	return NULL;
135 }
136 
137 /* Next are the signal handlers. */
138 /* This one is registered for signal SIGUSR1 */
sighdl1(int sig)139 void sighdl1(int sig)
140 {
141 #ifdef WITH_SYNCHRO
142 	if (sem_post(&semsig1))
143 	{ UNRESOLVED(errno, "Sem_post in signal handler 1"); }
144 #endif
145 }
146 /* This one is registered for signal SIGUSR2 */
sighdl2(int sig)147 void sighdl2(int sig)
148 {
149 #ifdef WITH_SYNCHRO
150 	if (sem_post(&semsig2))
151 	{ UNRESOLVED(errno, "Sem_post in signal handler 2"); }
152 #endif
153 }
154 
155 /* The following function will wait on the cond
156  * it does check that no error code of EINTR is returned */
waiter(void * arg)157 void * waiter(void * arg)
158 {
159 	int ret;
160 
161 	/* We block the signals SIGUSR1 and SIGUSR2 for this THREAD */
162 	ret = pthread_sigmask(SIG_BLOCK, &usersigs, NULL);
163 	if (ret != 0)  {  UNRESOLVED(ret, "Unable to block SIGUSR1 and SIGUSR2 in signal thread");  }
164 
165 	ret = pthread_mutex_lock(&(data.mtx));
166 	if (ret != 0)  {  UNRESOLVED(ret, "Unable to lock mutex in waiter thread");  }
167 
168 	do
169 	{
170 		ret = pthread_cond_wait(&(data.cnd),&(data.mtx));
171 		count_cnd_wup++;
172 	} while ((ret == 0) && (do_it != 0));
173 	if (ret != 0)
174 	{
175 		UNRESOLVED(ret, "pthread_cond_wait returned an unexpected error");
176 	}
177 	woken++;
178 
179 	ret = pthread_mutex_unlock(&(data.mtx));
180 	if (ret != 0)  {  UNRESOLVED(ret, "Unable to unlock mutex in waiter thread");  }
181 
182 	return NULL;
183 }
184 
185 
186 /* The next function will signal the condition */
worker(void * arg)187 void * worker (void * arg)
188 {
189 	int ret=0;
190 
191  	/* We don't block the signals SIGUSR1 and SIGUSR2 for this THREAD */
192 	ret = pthread_sigmask(SIG_UNBLOCK, &usersigs, NULL);
193 	if (ret != 0)  {  UNRESOLVED(ret, "Unable to unblock SIGUSR1 and SIGUSR2 in worker thread");  }
194 
195 	while (woken<5)
196 	{
197 		ret = pthread_cond_broadcast(&(data.cnd));
198 		if (ret == EINTR)  {  FAILED("pthread_cond_signal returned EINTR");  }
199 		if (ret != 0)  {  UNRESOLVED(ret, "Failed to signal the condition");  }
200 		count_cnd_sig++;
201 	}
202 
203 	return NULL;
204 }
205 
206 /* Main function */
main(int argc,char * argv[])207 int main (int argc, char * argv[])
208 {
209 	int ret,i;
210 	pthread_t th_waiter[5], th_worker, th_sig1, th_sig2;
211 	thestruct arg1, arg2;
212 	struct sigaction sa;
213 
214 	output_init();
215 
216 	/* We need to register the signal handlers for the PROCESS */
217 	sigemptyset (&sa.sa_mask);
218 	sa.sa_flags = 0;
219 	sa.sa_handler = sighdl1;
220 	if ((ret = sigaction (SIGUSR1, &sa, NULL)))
221 	{ UNRESOLVED(ret, "Unable to register signal handler1"); }
222 	sa.sa_handler = sighdl2;
223 	if ((ret = sigaction (SIGUSR2, &sa, NULL)))
224 	{ UNRESOLVED(ret, "Unable to register signal handler2"); }
225 
226 	/* We prepare a signal set which includes SIGUSR1 and SIGUSR2 */
227 	sigemptyset(&usersigs);
228 	ret = sigaddset(&usersigs, SIGUSR1);
229 	ret |= sigaddset(&usersigs, SIGUSR2);
230 	if (ret != 0)  {  UNRESOLVED(ret, "Unable to add SIGUSR1 or 2 to a signal set");  }
231 
232 	/* We now block the signals SIGUSR1 and SIGUSR2 for this THREAD */
233 	ret = pthread_sigmask(SIG_BLOCK, &usersigs, NULL);
234 	if (ret != 0)  {  UNRESOLVED(ret, "Unable to block SIGUSR1 and SIGUSR2 in main thread");  }
235 
236 
237 
238 	#ifdef WITH_SYNCHRO
239 	if (sem_init(&semsig1, 0, 1))
240 	{ UNRESOLVED(errno, "Semsig1  init"); }
241 	if (sem_init(&semsig2, 0, 1))
242 	{ UNRESOLVED(errno, "Semsig2  init"); }
243 	#endif
244 
245 	for (i=0; i<5; i++)
246 	{
247 		if ((ret = pthread_create(&th_waiter[i], NULL, waiter, NULL)))
248 		{ UNRESOLVED(ret, "Waiter thread creation failed"); }
249 	}
250 
251 	if ((ret = pthread_create(&th_worker, NULL, worker, NULL)))
252 	{ UNRESOLVED(ret, "Worker thread creation failed"); }
253 
254 	arg1.sig = SIGUSR1;
255 	arg2.sig = SIGUSR2;
256 #ifdef WITH_SYNCHRO
257 	arg1.sem = &semsig1;
258 	arg2.sem = &semsig2;
259 #endif
260 
261 	if ((ret = pthread_create(&th_sig1, NULL, sendsig, (void *)&arg1)))
262 	{ UNRESOLVED(ret, "Signal 1 sender thread creation failed"); }
263 	if ((ret = pthread_create(&th_sig2, NULL, sendsig, (void *)&arg2)))
264 	{ UNRESOLVED(ret, "Signal 2 sender thread creation failed"); }
265 
266 	/* Let's wait for a while now */
267 	sleep(1);
268 
269 	/* Now stop the threads and join them */
270 	do { do_it=0; }
271 	while (do_it);
272 
273 	if ((ret = pthread_join(th_sig1, NULL)))
274 	{ UNRESOLVED(ret, "Signal 1 sender thread join failed"); }
275 	if ((ret = pthread_join(th_sig2, NULL)))
276 	{ UNRESOLVED(ret, "Signal 2 sender thread join failed"); }
277 	for (i=0; i<5; i++)
278 	{
279 		if ((ret = pthread_join(th_waiter[i], NULL)))
280 		{ UNRESOLVED(ret, "Waiter thread join failed"); }
281 	}
282 	if ((ret = pthread_join(th_worker, NULL)))
283 	{ UNRESOLVED(ret, "Worker thread join failed"); }
284 
285 	#if VERBOSE > 0
286 	output("Test executed successfully.\n");
287 	output("  Condition was signaled %d times.\n", count_cnd_sig);
288 	output("  pthread_cond_wait exited %d times.\n", count_cnd_wup);
289 	#ifdef WITH_SYNCHRO
290 	output("  %d signals were sent meanwhile.\n", count_sig);
291 	#endif
292 	#endif
293 	PASSED;
294 }
295