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