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 function is a cancelation point: when cancelability
21 * is PTHREAD_CANCEL_DEFERRED and a cancel request arrives, the thread
22 * must relock the mutex before the first (if any) clean up handler is called.
23
24 * The steps are:
25 * -> Create a thread
26 *   ->  this thread locks a mutex then waits for a condition
27 * -> cancel the thread
28 *   -> the cancelation handler will test if the thread owns the mutex.
29 */
30
31 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
32 #define _POSIX_C_SOURCE 200112L
33
34 /* We need the XSI extention for the mutex attributes */
35#ifndef WITHOUT_XOPEN
36 #define _XOPEN_SOURCE	600
37#endif
38 /********************************************************************************************/
39/****************************** standard includes *****************************************/
40/********************************************************************************************/
41 #include <pthread.h>
42 #include <stdarg.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46
47 #include <errno.h>
48 #include <time.h>
49 #include <semaphore.h>
50
51/********************************************************************************************/
52/******************************   Test framework   *****************************************/
53/********************************************************************************************/
54 #include "testfrmw.h"
55 #include "testfrmw.c"
56 /* This header is responsible for defining the following macros:
57  * UNRESOLVED(ret, descr);
58  *    where descr is a description of the error and ret is an int (error code for example)
59  * FAILED(descr);
60  *    where descr is a short text saying why the test has failed.
61  * PASSED();
62  *    No parameter.
63  *
64  * Both three macros shall terminate the calling process.
65  * The testcase shall not terminate in any other maneer.
66  *
67  * The other file defines the functions
68  * void output_init()
69  * void output(char * string, ...)
70  *
71  * Those may be used to output information.
72  */
73
74/********************************************************************************************/
75/********************************** Configuration ******************************************/
76/********************************************************************************************/
77#ifndef VERBOSE
78#define VERBOSE 1
79#endif
80
81#ifndef WITHOUT_ALTCLK
82#define USE_ALTCLK  /* make tests with MONOTONIC CLOCK if supported */
83#endif
84
85/********************************************************************************************/
86/***********************************    Test case   *****************************************/
87/********************************************************************************************/
88
89struct
90{
91	pthread_mutex_t mtx;
92	pthread_cond_t cnd;
93	int type;
94	sem_t semA;
95	sem_t semB;
96	int bool;
97} data;
98
99
100/****  First handler that will be poped
101 *  This one works only with recursive mutexes
102 */
103void clnp1 (void * arg)
104{
105	int ret;
106	if (data.type == PTHREAD_MUTEX_RECURSIVE)
107	{
108		ret = pthread_mutex_trylock(&(data.mtx));
109		if (ret != 0)  {  FAILED("Unable to double-lock a recursive mutex in clean-up handler 1");  }
110		ret = pthread_mutex_unlock(&(data.mtx));
111		if (ret != 0)  {  UNRESOLVED(ret, "Unable to unlock double-locked recursive mutex in clean-up handler 1");  }
112	}
113	return;
114}
115
116/**** Second handler
117 *  This one will trigger an action in main thread, while we are owning the mutex
118 */
119void clnp2 (void * arg)
120{
121	int ret;
122	do {
123		ret = sem_post(&(data.semA));
124	} while ((ret != 0) && (errno == EINTR));
125	if (ret != 0)  {  UNRESOLVED(errno, "Sem post failed in cleanup handler 2");  }
126
127	do {
128		ret = sem_wait(&(data.semB));
129	} while ((ret != 0) && (errno == EINTR));
130	if (ret != 0)  {  UNRESOLVED(errno, "Sem wait failed in cleanup handler 2");  }
131
132	return;
133}
134
135/**** Third handler
136 *  Will actually unlock the mutex, then try to unlock second time to check an error is returned
137 */
138void clnp3 (void * arg)
139{
140	int ret;
141
142	ret = pthread_mutex_unlock(&(data.mtx));
143	if (ret != 0)  {  UNRESOLVED(ret, "Unable to unlock mutex in clean-up handler 3");  }
144
145	if ((data.type == PTHREAD_MUTEX_ERRORCHECK) || (data.type == PTHREAD_MUTEX_RECURSIVE))
146	{
147		ret = pthread_mutex_unlock(&(data.mtx));
148		if (ret == 0)  {  UNRESOLVED(ret, "Was able to unlock unlocked mutex in clean-up handler 3");  }
149	}
150
151	return;
152}
153
154
155/**** Thread function
156 * This function will lock the mutex, then install the cleanup handlers
157 * and wait for the cond. At this point it will be canceled.
158 */
159void * threaded (void * arg)
160{
161	int ret;
162
163	ret= pthread_mutex_lock(&(data.mtx));
164	if (ret != 0)  {  UNRESOLVED(ret, "Failed to lock the mutex in thread");  }
165
166	/* Tell the main thread we got the lock */
167	do {
168		ret = sem_post(&(data.semA));
169	} while ((ret != 0) && (errno == EINTR));
170	if (ret != 0)  {  UNRESOLVED(errno, "Sem post failed in cleanup handler 2");  }
171
172	pthread_cleanup_push ( clnp3, NULL);
173	pthread_cleanup_push ( clnp2, NULL);
174	pthread_cleanup_push ( clnp1, NULL);
175
176	do {
177		ret = pthread_cond_wait(&(data.cnd), &(data.mtx));
178	} while ((ret == 0) && (data.bool == 0));
179
180	if (ret != 0)  {  UNRESOLVED(ret , "Wait failed");  }  /* We will exit even if the error is timedwait */
181
182	/* If we are here, the thread was not canceled */
183	FAILED("The thread has not been canceled");
184
185	pthread_cleanup_pop(0);
186	pthread_cleanup_pop(0);
187	pthread_cleanup_pop(1);
188
189	return NULL;
190}
191
192int main(int argc, char * argv[])
193{
194	int ret, i;
195	void * rc;
196
197	pthread_mutexattr_t ma;
198	pthread_condattr_t ca;
199	pthread_t th;
200
201	long altclk_ok, pshared_ok;
202
203	struct
204	{
205		char altclk;  /* Want to use alternative clock */
206		char pshared; /* Want to use process-shared primitives */
207		int type;     /* mutex type */
208		char * descr; /* Description of the case */
209
210	} scenar[] =
211	{ {0, 0, PTHREAD_MUTEX_NORMAL,     "Normal mutex" }
212#ifdef USE_ALTCLK
213	 ,{1, 0, PTHREAD_MUTEX_NORMAL,     "Normal mutex + altclock cond" }
214	 ,{1, 1, PTHREAD_MUTEX_NORMAL,     "PShared mutex + altclock cond" }
215#endif
216	 ,{0, 1, PTHREAD_MUTEX_NORMAL,     "Pshared mutex" }
217#ifndef WITHOUT_XOPEN
218	 ,{0, 0, PTHREAD_MUTEX_ERRORCHECK, "Errorcheck mutex" }
219	 ,{0, 0, PTHREAD_MUTEX_RECURSIVE , "Recursive mutex"  }
220#ifdef USE_ALTCLK
221	 ,{1, 0, PTHREAD_MUTEX_RECURSIVE , "Recursive mutex + altclock cond" }
222	 ,{1, 0, PTHREAD_MUTEX_ERRORCHECK, "Errorcheck mutex + altclock cond" }
223	 ,{1, 1, PTHREAD_MUTEX_RECURSIVE , "Recursive pshared mutex + altclock cond" }
224	 ,{1, 1, PTHREAD_MUTEX_ERRORCHECK, "Errorcheck pshared mutex + altclock cond" }
225#endif
226	 ,{0, 1, PTHREAD_MUTEX_RECURSIVE , "Recursive pshared mutex" }
227	 ,{0, 1, PTHREAD_MUTEX_ERRORCHECK, "Errorcheck pshared mutex" }
228#endif
229	};
230
231	output_init();
232
233	/* Initialize the constants */
234	altclk_ok = sysconf(_SC_CLOCK_SELECTION);
235	if (altclk_ok > 0)
236		altclk_ok = sysconf(_SC_MONOTONIC_CLOCK);
237#ifndef USE_ALTCLK
238	if (altclk_ok > 0)
239		output("Implementation supports the MONOTONIC CLOCK but option is disabled in test.\n");
240#endif
241
242	pshared_ok = sysconf(_SC_THREAD_PROCESS_SHARED);
243
244	#if VERBOSE > 0
245	output("Test starting\n");
246	output(" Process-shared primitive %s be tested\n", (pshared_ok>0)?"will":"won't");
247	output(" Alternative clock for cond %s be tested\n", (altclk_ok>0)?"will":"won't");
248	#endif
249
250	ret = sem_init(&(data.semA), 0, 0);
251	if (ret != 0)  {  UNRESOLVED(errno, "Unable to init sem A");  }
252
253	ret = sem_init(&(data.semB), 0, 0);
254	if (ret != 0)  {  UNRESOLVED(errno, "Unable to init sem B");  }
255
256	for (i=0; i< (sizeof(scenar) / sizeof(scenar[0])); i++)
257	{
258		#if VERBOSE > 1
259		output("Starting test for %s\n", scenar[i].descr);
260		#endif
261
262		/* Initialize the data structure */
263		ret = pthread_mutexattr_init(&ma);
264		if (ret != 0)  {  UNRESOLVED(ret, "Mutex attribute object init failed");  }
265
266		ret = pthread_mutexattr_settype(&ma, scenar[i].type);
267		if (ret != 0)  {  UNRESOLVED(ret, "Unable to set mutex type");  }
268
269		if ((pshared_ok > 0) && (scenar[i].pshared != 0))
270		{
271			ret = pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED);
272			if (ret != 0)  {  UNRESOLVED(ret, "Unable to set mutex process-shared");  }
273		}
274
275		ret = pthread_condattr_init(&ca);
276		if (ret != 0)  {  UNRESOLVED(ret, "Cond attribute object init failed");  }
277
278		if ((pshared_ok > 0) && (scenar[i].pshared != 0))
279		{
280			ret = pthread_condattr_setpshared(&ca, PTHREAD_PROCESS_SHARED);
281			if (ret != 0)  {  UNRESOLVED(ret, "Unable to set cond process-shared");  }
282		}
283
284		#ifdef USE_ALTCLK
285		if ((altclk_ok > 0) && (scenar[i].altclk != 0))
286		{
287			ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC);
288			if (ret != 0)  {  UNRESOLVED(ret, "Unable to set alternative (monotonic) clock for cond");  }
289		}
290		#endif
291
292		ret = pthread_mutex_init(&(data.mtx), &ma);
293		if (ret != 0)  {  UNRESOLVED(ret, "Unable to init mutex");  }
294
295		ret = pthread_cond_init(&(data.cnd), &ca);
296		if (ret != 0)  {  UNRESOLVED(ret, "Unable to initialize condvar");  }
297
298		ret = pthread_mutexattr_gettype(&ma, &(data.type));
299		if (ret != 0)  {  UNRESOLVED(ret, "Unable to get type from mutex attr");  }
300
301		data.bool = 0;
302
303		/** Data is ready, create the thread */
304		#if VERBOSE > 1
305		output("Initialization OK, starting thread\n");
306		#endif
307
308		ret = pthread_create(&th, NULL, threaded, NULL);
309		if (ret != 0)  {  UNRESOLVED(ret, "Thread creation failed");  }
310
311		/** Wait for the thread to be waiting */
312		do {
313			ret = sem_wait(&(data.semA));
314		} while ((ret != 0) && (errno == EINTR));
315		if (ret != 0)  {  UNRESOLVED(errno, "Sem wait failed in main");  }
316
317		ret = pthread_mutex_lock(&(data.mtx));
318		if (ret != 0)  {  UNRESOLVED(ret, "Unable to lock mutex in main");  }
319
320		data.bool = 1;
321
322		/** Cancel the thread */
323		ret = pthread_cancel(th);
324		if (ret != 0)  {  UNRESOLVED(ret, "Thread cancelation failed");  }
325
326		sched_yield();
327		#ifndef WITHOUT_XOPEN
328		usleep(100);
329		#endif
330
331		ret = pthread_mutex_unlock(&(data.mtx));
332		if (ret != 0)  {  UNRESOLVED(ret, "Unable to unlock mutex in main");  }
333
334		/** Wait for the thread to be executing second cleanup handler */
335		do {
336			ret = sem_wait(&(data.semA));
337		} while ((ret != 0) && (errno == EINTR));
338		if (ret != 0)  {  UNRESOLVED(errno, "Sem wait failed in main");  }
339
340		/** Here the child should own the mutex, we check this */
341		ret = pthread_mutex_trylock(&(data.mtx));
342		if (ret == 0)  {  FAILED("The child did not own the mutex inside the cleanup handler");  }
343
344		/** Let the cleanups go on */
345		do {
346			ret = sem_post(&(data.semB));
347		} while ((ret != 0) && (errno == EINTR));
348		if (ret != 0)  {  UNRESOLVED(errno, "Sem post failed in main");  }
349
350		/** Join the thread */
351		ret = pthread_join(th, &rc);
352		if (ret != 0)  {  UNRESOLVED(ret, "Unable to join the thread");  }
353		if (rc != PTHREAD_CANCELED)  {  FAILED("thread was not canceled");  }
354
355		#if VERBOSE > 1
356		output("Test passed for %s\n\n", scenar[i].descr);
357		#endif
358
359		/* Destroy datas */
360		ret = pthread_cond_destroy(&(data.cnd));
361		if (ret != 0)  {  UNRESOLVED(ret, "Cond destroy failed");  }
362
363		ret = pthread_mutex_destroy(&(data.mtx));
364		if (ret != 0)  {  UNRESOLVED(ret, "Mutex destroy failed");  }
365
366		ret = pthread_condattr_destroy(&ca);
367		if (ret != 0)  {  UNRESOLVED(ret, "Cond attribute destroy failed");  }
368
369		ret = pthread_mutexattr_destroy(&ma);
370		if (ret != 0)  {  UNRESOLVED(ret, "Mutex attr destroy failed");  }
371	} /* Proceed to next case */
372
373	ret = sem_destroy(&(data.semA));
374	if (ret != 0)  {  UNRESOLVED(errno, "Sem destroy failed");  }
375
376	ret = sem_destroy(&(data.semB));
377	if (ret != 0)  {  UNRESOLVED(errno, "Sem destroy failed");  }
378
379	PASSED;
380}
381
382