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
83char do_it=1;
84char woken=0;
85unsigned long count_cnd_sig=0, count_cnd_wup=0;
86#ifdef WITH_SYNCHRO
87sem_t semsig1;
88sem_t semsig2;
89unsigned long count_sig=0;
90#endif
91
92sigset_t usersigs;
93
94typedef struct
95{
96	int	sig;
97#ifdef WITH_SYNCHRO
98	sem_t	*sem;
99#endif
100} thestruct;
101
102struct
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 */
109void * 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 */
139void 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 */
147void 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 */
157void * 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 */
187void * 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 */
207int 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