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