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 * If the mutex attribute pointer passed to pthread_mutex_init is NULL,
21 * the effects on the mutex are the same as if a default mutex attribute object had been passed.
22 *
23 * The steps are:
24 *  * create two mutexes. One is initialized with NULL attribute, the other with a default attribute object.
25 *  * Compare the following features between the two mutexes:
26 *      -> Can it cause / detect a deadlock? (attempt to lock a mutex the thread already owns).
27 *            If detected, do both mutexes cause the same error code?
28 *      -> Is an error returned when unlocking the mutex in unlocked state?
29 *            When unlocking the mutex owned by another thread?
30 *
31 * The test will pass if the results of each feature are the same for the two mutexes
32 * (making no assumption on what is the default behavior).
33 * The test will be unresolved if any initialization fails.
34 * The test will fail if a feature differs between the two mutex objects.
35 */
36
37 /*
38  * - adam.li@intel.com 2004-05-09
39  *   Add to PTS. Please refer to http://nptl.bullopensource.org/phpBB/
40  *   for general information
41  */
42
43
44 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
45 #define _POSIX_C_SOURCE 200112L
46/********************************************************************************************/
47/****************************** standard includes *****************************************/
48/********************************************************************************************/
49 #include <pthread.h>
50 #include <semaphore.h>
51 #include <errno.h>
52 #include <stdio.h>
53 #include <unistd.h>
54 #include <stdarg.h>
55 #include <stdlib.h>
56
57/********************************************************************************************/
58/******************************   Test framework   *****************************************/
59/********************************************************************************************/
60 #include "testfrmw.h"
61 #include "testfrmw.c"
62 /* This header is responsible for defining the following macros:
63  * UNRESOLVED(ret, descr);
64  *    where descr is a description of the error and ret is an int (error code for example)
65  * FAILED(descr);
66  *    where descr is a short text saying why the test has failed.
67  * PASSED();
68  *    No parameter.
69  *
70  * Both three macros shall terminate the calling process.
71  * The testcase shall not terminate in any other maneer.
72  *
73  * The other file defines the functions
74  * void output_init()
75  * void output(char * string, ...)
76  *
77  * Those may be used to output information.
78  */
79
80/********************************************************************************************/
81/********************************** Configuration ******************************************/
82/********************************************************************************************/
83#ifndef VERBOSE
84#define VERBOSE 1
85#endif
86
87/********************************************************************************************/
88/***********************************    Test case   *****************************************/
89/********************************************************************************************/
90
91/**** global variables ****/
92pthread_mutex_t * p_mtx;
93int retval = 0;
94int returned = 0;
95int canceled = 0;
96sem_t semA, semB;
97
98/***** Cancelation handlers  *****/
99void cleanup_deadlk(void * arg)
100{
101	canceled = 1;
102	pthread_mutex_unlock(p_mtx);
103}
104
105/***** Threads functions *****/
106void * deadlk_issue(void * arg)
107{
108	int ret, tmp;
109
110	if ((ret=pthread_mutex_lock(p_mtx)))
111	{ UNRESOLVED(ret, "First mutex lock in deadlk_issue"); }
112	pthread_cleanup_push(cleanup_deadlk, NULL);
113	if ((ret = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &tmp)))
114	{ UNRESOLVED(ret, "Set cancel type in deadlk_issue"); }
115	if ((ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &tmp)))
116	{ UNRESOLVED(ret, "Set cancel state in deadlk_issue"); }
117	#if VERBOSE >1
118	output("Thread releases the semaphore...\n");
119	#endif
120	if ((ret = sem_post(&semA)))
121	{ UNRESOLVED(errno, "Sem_post in deadlk_issue"); }
122
123    	returned = 0;
124	retval = pthread_mutex_lock(p_mtx);
125	returned = 1;
126
127	if ((ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &tmp)))
128	{ UNRESOLVED(ret, "Set cancel state in deadlk_issue"); }
129	pthread_cleanup_pop(0);
130	return NULL;
131}
132
133void * unlock_issue(void * arg)
134{
135	int ret;
136
137	#if VERBOSE >1
138	output("Locking in child...\n");
139	#endif
140	if ((ret=pthread_mutex_lock(p_mtx)))
141	{ UNRESOLVED(ret, "First mutex lock in unlock_issue"); }
142
143	if ((ret = sem_post(&semA)))
144	{ UNRESOLVED(errno, "Sem_post in unlock_issue"); }
145
146	if ((ret = sem_wait(&semB)))
147	{ UNRESOLVED(errno, "Sem_wait in unlock_issue"); }
148
149	if (retval != 0) /* parent thread failed to unlock the mutex) */
150	{
151		#if VERBOSE >1
152		output("Unlocking in child...\n");
153		#endif
154		if ((ret=pthread_mutex_unlock(p_mtx)))
155		{ FAILED("Mutex unlock returned an error but mutex is unlocked."); }
156	}
157
158	return NULL;
159}
160
161
162/***** main program *****/
163int main(int argc, char *argv[])
164{
165	pthread_mutex_t mtx_null, mtx_def;
166	pthread_mutexattr_t mattr;
167	pthread_t thr;
168
169	pthread_mutex_t * tab_mutex[2]={&mtx_null, &mtx_def};
170	int tab_res[2][3]={{0,0,0},{0,0,0}};
171
172	int ret;
173	void * th_ret;
174
175	int i;
176
177	output_init();
178
179	#if VERBOSE >1
180	output("Test starting...\n");
181	#endif
182
183	/* We first initialize the two mutexes. */
184	if ((ret=pthread_mutex_init(&mtx_null, NULL)))
185	{ UNRESOLVED(ret, "NULL mutex init"); }
186
187	if ((ret=pthread_mutexattr_init(&mattr)))
188	{ UNRESOLVED(ret, "Mutex attribute init"); }
189	if ((ret=pthread_mutex_init(&mtx_def, &mattr)))
190	{ UNRESOLVED(ret, "Default attribute mutex init"); }
191
192	if ((ret=pthread_mutexattr_destroy(&mattr)))
193	{ UNRESOLVED(ret, "Mutex attribute destroy"); }
194
195
196	if ((ret=sem_init(&semA, 0, 0)))
197	{ UNRESOLVED(errno, "Sem A init"); }
198	if ((ret=sem_init(&semB, 0, 0)))
199	{ UNRESOLVED(errno, "Sem B init"); }
200
201	#if VERBOSE >1
202	output("Data initialized...\n");
203	#endif
204
205
206	/* OK let's go for the first part of the test : abnormals unlocking */
207
208	/* We first check if unlocking an unlocked mutex returns an error. */
209	retval = pthread_mutex_unlock(tab_mutex[0]);
210	ret = pthread_mutex_unlock(tab_mutex[1]);
211	#if VERBOSE >0
212	output("Results for unlock issue #1:\n mutex 1 unlocking returned %i\n mutex 2 unlocking returned %i\n",
213				retval, ret);
214	#endif
215	if (ret != retval)
216	{
217		FAILED("Unlocking an unlocked mutex behaves differently.");
218	}
219
220    /* Now we focus on unlocking a mutex lock by another thread */
221	for (i=0; i<2; i++)
222	{
223		p_mtx = tab_mutex[i];
224		tab_res[i][0]=0;
225		tab_res[i][1]=0;
226		tab_res[i][2]=0;
227
228		#if VERBOSE >1
229		output("Creating thread (unlock)...\n");
230		#endif
231
232		if ((ret = pthread_create(&thr, NULL, unlock_issue, NULL)))
233		{ UNRESOLVED(ret, "Unlock issue thread create"); }
234
235		if ((ret = sem_wait(&semA)))
236		{ UNRESOLVED(errno, "Sem A wait failed for unlock issue."); }
237
238		#if VERBOSE >1
239		output("Unlocking in parent...\n");
240		#endif
241		retval = pthread_mutex_unlock(p_mtx);
242
243		if ((ret = sem_post(&semB)))
244		{ UNRESOLVED(errno, "Sem B post failed for unlock issue."); }
245
246		if ((ret=pthread_join(thr, &th_ret)))
247		{ UNRESOLVED(ret, "Join thread"); }
248
249		#if VERBOSE >1
250		output("Thread joined successfully...\n");
251		#endif
252
253		tab_res[i][0] = retval;
254	}
255	#if VERBOSE >0
256	output("Results for unlock issue #2:\n mutex 1 returned %i\n mutex 2 returned %i\n",
257				tab_res[0][0],tab_res[1][0]);
258	#endif
259
260	if (tab_res[0][0] != tab_res[1][0])
261	{
262		FAILED("Unlocking an unowned mutex behaves differently.");
263	}
264
265
266	/* We now are going to test the deadlock issue
267	 */
268
269	/* We start with testing the NULL mutex features */
270	for (i=0; i<2; i++)
271	{
272		p_mtx = tab_mutex[i];
273		tab_res[i][0]=0;
274		tab_res[i][1]=0;
275		tab_res[i][2]=0;
276
277		#if VERBOSE >1
278		output("Creating thread (deadlk)...\n");
279		#endif
280
281		if ((ret = pthread_create(&thr, NULL, deadlk_issue, NULL)))
282		{ UNRESOLVED(ret, "Deadlk_issue thread create"); }
283
284		/* Now we are waiting the thread is ready to relock the mutex. */
285		if ((ret=sem_wait(&semA)))
286		{ UNRESOLVED(errno, "Sem wait"); }
287
288		/* To ensure thread runs until second lock, we yield here */
289		sched_yield();
290
291		/* OK, now we cancel the thread */
292		canceled=0;
293		#if VERBOSE >1
294		output("Cancel thread...\n");
295		#endif
296		if (returned ==0)
297			if ((ret=pthread_cancel(thr)))
298			{ UNRESOLVED(ret, "Cancel thread (deadlk_issue)"); }
299
300		#if VERBOSE >1
301		output("Thread canceled...\n");
302		#endif
303
304		if ((ret=pthread_join(thr, &th_ret)))
305		{ UNRESOLVED(ret, "Join thread"); }
306
307		#if VERBOSE >1
308		output("Thread joined successfully...\n");
309		#endif
310
311		tab_res[i][2] = retval;
312		tab_res[i][1] = returned;
313		tab_res[i][0] = canceled;
314	}
315
316	/* Now we parse the results */
317	#if VERBOSE >0
318	output("Results for deadlock issue:\n mutex 1 \t%s\t%s%i\n mutex 2 \t%s\t%s%i\n",
319				tab_res[0][0]?"deadlock" : "no deadlock",
320				tab_res[0][1]?"returned " : "did not return ",
321				tab_res[0][2],
322				tab_res[1][0]?"deadlock" : "no deadlock",
323				tab_res[1][1]?"returned " : "did not return ",
324				tab_res[1][2]);
325	#endif
326
327	if (tab_res[0][0] != tab_res[1][0])
328	{ FAILED("One mutex deadlocks, not the other"); }
329
330	if (tab_res[0][1] != tab_res[1][1])
331	{ UNRESOLVED(tab_res[0][1], "Abnormal situation!"); }
332
333	if ((tab_res[0][1] == 1) && (tab_res[0][2] != tab_res[1][2]))
334	{ FAILED("The locks returned different error codes."); }
335
336	PASSED;
337}
338