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