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 was already locked, the call returns EBUSY immediatly.
21
22 * The steps are:
23 * -> Set a timeout.
24 * -> For each kind of mutex,
25 *   -> Lock the mutex.
26 *   -> create a new child (either thread or process)
27 *      -> the new child trylock the mutex. It shall fail.
28 *   -> undo everything.
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   and the mkstemp() routine */
36#ifndef WITHOUT_XOPEN
37 #define _XOPEN_SOURCE	600
38#endif
39 /********************************************************************************************/
40/****************************** standard includes *****************************************/
41/********************************************************************************************/
42 #include <pthread.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <unistd.h>
47
48 #include <errno.h>
49 #include <sys/wait.h>
50 #include <sys/mman.h>
51 #include <string.h>
52
53/********************************************************************************************/
54/******************************   Test framework   *****************************************/
55/********************************************************************************************/
56 #include "testfrmw.h"
57 #include "testfrmw.c"
58 /* This header is responsible for defining the following macros:
59  * UNRESOLVED(ret, descr);
60  *    where descr is a description of the error and ret is an int (error code for example)
61  * FAILED(descr);
62  *    where descr is a short text saying why the test has failed.
63  * PASSED();
64  *    No parameter.
65  *
66  * Both three macros shall terminate the calling process.
67  * The testcase shall not terminate in any other maneer.
68  *
69  * The other file defines the functions
70  * void output_init()
71  * void output(char * string, ...)
72  *
73  * Those may be used to output information.
74  */
75
76/********************************************************************************************/
77/********************************** Configuration ******************************************/
78/********************************************************************************************/
79#ifndef VERBOSE
80#define VERBOSE 1
81#endif
82
83/********************************************************************************************/
84/***********************************    Test case   *****************************************/
85/********************************************************************************************/
86typedef struct
87{
88	pthread_mutex_t mtx;
89	int status; /* error code */
90} testdata_t;
91
92struct _scenar
93{
94	int m_type; /* Mutex type to use */
95	int m_pshared; /* 0: mutex is process-private (default) ~ !0: mutex is process-shared, if supported */
96	int fork; /* 0: Test between threads. ~ !0: Test across processes, if supported (mmap) */
97	char * descr; /* Case description */
98}
99scenarii[] =
100{
101	 {PTHREAD_MUTEX_DEFAULT,    0, 0, "Default mutex"}
102#ifndef WITHOUT_XOPEN
103	,{PTHREAD_MUTEX_NORMAL,     0, 0, "Normal mutex"}
104	,{PTHREAD_MUTEX_ERRORCHECK, 0, 0, "Errorcheck mutex"}
105	,{PTHREAD_MUTEX_RECURSIVE,  0, 0, "Recursive mutex"}
106#endif
107
108	,{PTHREAD_MUTEX_DEFAULT,    1, 0, "Pshared mutex"}
109#ifndef WITHOUT_XOPEN
110	,{PTHREAD_MUTEX_NORMAL,     1, 0, "Pshared Normal mutex"}
111	,{PTHREAD_MUTEX_ERRORCHECK, 1, 0, "Pshared Errorcheck mutex"}
112	,{PTHREAD_MUTEX_RECURSIVE,  1, 0, "Pshared Recursive mutex"}
113#endif
114
115	,{PTHREAD_MUTEX_DEFAULT,    1, 1, "Pshared mutex across processes"}
116#ifndef WITHOUT_XOPEN
117	,{PTHREAD_MUTEX_NORMAL,     1, 1, "Pshared Normal mutex across processes"}
118	,{PTHREAD_MUTEX_ERRORCHECK, 1, 1, "Pshared Errorcheck mutex across processes"}
119	,{PTHREAD_MUTEX_RECURSIVE,  1, 1, "Pshared Recursive mutex across processes"}
120#endif
121};
122#define NSCENAR (sizeof(scenarii)/sizeof(scenarii[0]))
123
124/* The test function will only perform a trylock operation then return. */
125void * tf(void * arg)
126{
127	testdata_t * td = (testdata_t *)arg;
128
129	td->status = pthread_mutex_trylock(&(td->mtx));
130
131	if (td->status == 0)
132	{
133		int ret;
134
135		ret = pthread_mutex_unlock(&(td->mtx));
136		if (ret != 0)  {  UNRESOLVED(ret, "Failed to unlock a locked semaphore");  }
137	}
138
139	return NULL;
140}
141
142/* Main entry point. */
143int main(int argc, char * argv[])
144{
145	int ret;
146	int sc;
147	pthread_mutexattr_t ma;
148
149	testdata_t * td;
150	testdata_t alternativ;
151
152	int do_fork;
153
154	pid_t child_pr=0, chkpid;
155	int status;
156	pthread_t child_th;
157
158	long pshared, mf;
159
160	/* Initialize output */
161	output_init();
162
163	/* Initialize the timeout */
164	alarm(30);
165
166	/* Test system abilities */
167	pshared = sysconf(_SC_THREAD_PROCESS_SHARED);
168	mf =sysconf(_SC_MAPPED_FILES);
169
170	#if VERBOSE > 0
171	output("Test starting\n");
172	output("System abilities:\n");
173	output(" TSH : %li\n", pshared);
174	output(" MF  : %li\n", mf);
175	if ((mf < 0) || (pshared < 0))
176		output("Process-shared attributes won't be tested\n");
177	#endif
178
179	#ifdef WITHOUT_XOPEN
180	#if VERBOSE > 0
181	output("As XSI extension is disabled, we won't test the feature across process\n");
182	#endif
183	mf = -1;
184	#endif
185
186/**********
187 * Allocate space for the testdata structure
188 */
189	if (mf < 0)
190	{
191		/* Cannot mmap a file (or not interested in this), we use an alternative method */
192		td = &alternativ;
193		pshared = -1; /* We won't do this testing anyway */
194		#if VERBOSE > 0
195		output("Testdata allocated in the process memory.\n");
196		#endif
197	}
198	#ifndef WITHOUT_XOPEN
199	else
200	{
201		/* We will place the test data in a mmaped file */
202		char filename[] = "/tmp/mutex_trylock_4-2-XXXXXX";
203		size_t sz;
204		void * mmaped;
205		int fd;
206		char * tmp;
207
208		/* We now create the temp files */
209		fd = mkstemp(filename);
210		if (fd == -1)
211		{ UNRESOLVED(errno, "Temporary file could not be created"); }
212
213		/* and make sure the file will be deleted when closed */
214		unlink(filename);
215
216		#if VERBOSE > 1
217		output("Temp file created (%s).\n", filename);
218		#endif
219
220		sz= (size_t)sysconf(_SC_PAGESIZE);
221
222		tmp = calloc(1, sz);
223		if (tmp == NULL)
224		{ UNRESOLVED(errno, "Memory allocation failed"); }
225
226		/* Write the data to the file.  */
227		if (write (fd, tmp, sz) != (ssize_t) sz)
228		{ UNRESOLVED(sz, "Writting to the file failed"); }
229
230		free(tmp);
231
232		/* Now we can map the file in memory */
233		mmaped = mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
234		if (mmaped == MAP_FAILED)
235		{ UNRESOLVED(errno, "mmap failed"); }
236
237		td = (testdata_t *) mmaped;
238
239		/* Our datatest structure is now in shared memory */
240		#if VERBOSE > 1
241		output("Testdata allocated in shared memory.\n");
242		#endif
243	}
244	#endif
245
246/**********
247 * For each test scenario, initialize the attributes and other variables.
248 * Do the whole thing for each time to test.
249 */
250	for ( sc=0; sc < NSCENAR ; sc++)
251	{
252		#if VERBOSE > 1
253		output("[parent] Preparing attributes for: %s\n", scenarii[sc].descr);
254		#endif
255		/* set / reset everything */
256		do_fork=0;
257		ret = pthread_mutexattr_init(&ma);
258		if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to initialize the mutex attribute object");  }
259
260		#ifndef WITHOUT_XOPEN
261		/* Set the mutex type */
262		ret = pthread_mutexattr_settype(&ma, scenarii[sc].m_type);
263		if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to set mutex type");  }
264		#if VERBOSE > 1
265		output("[parent] Mutex type : %i\n", scenarii[sc].m_type);
266		#endif
267		#endif
268
269		/* Set the pshared attributes, if supported */
270		if ((pshared > 0) && (scenarii[sc].m_pshared != 0))
271		{
272			ret = pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED);
273			if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to set the mutex process-shared");  }
274			#if VERBOSE > 1
275			output("[parent] Mutex is process-shared\n");
276			#endif
277		}
278		#if VERBOSE > 1
279		else {
280			output("[parent] Mutex is process-private\n");
281		}
282		#endif
283
284		/* Tell whether the test will be across processes */
285		if ((pshared > 0) && (scenarii[sc].fork != 0))
286		{
287			do_fork = 1;
288			#if VERBOSE > 1
289			output("[parent] Child will be a new process\n");
290			#endif
291		}
292		#if VERBOSE > 1
293		else {
294			output("[parent] Child will be a new thread\n");
295		}
296		#endif
297
298/**********
299 * Initialize the testdata_t structure with the previously defined attributes
300 */
301		/* Initialize the mutex */
302		ret = pthread_mutex_init(&(td->mtx), &ma);
303		if (ret != 0)
304		{  UNRESOLVED(ret, "[parent] Mutex init failed");  }
305
306		/* Initialize the other datas from the test structure */
307		td->status=0;
308
309/**********
310 * Proceed to the actual testing
311 */
312		/* Trylock the mutex twice before creating children */
313		ret = pthread_mutex_lock(&(td->mtx));
314		if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to lock the mutex");  }
315
316		/* Create the children */
317		if (do_fork != 0)
318		{
319			/* We are testing across processes */
320			child_pr = fork();
321			if (child_pr == -1)
322			{  UNRESOLVED(errno, "[parent] Fork failed");  }
323
324			if (child_pr == 0)
325			{
326				#if VERBOSE > 3
327				output("[child] Child process is starting...\n");
328				#endif
329
330				if (tf((void *)td) != NULL)
331				{
332					UNRESOLVED( -1, "[child] Got an unexpected return value from test function");
333				}
334				else
335				{
336					/* We cannot use the PASSED macro here since it would terminate the output */
337					exit (0);
338				}
339			}
340			/* Only the parent process goes further */
341		}
342		else /* do_fork == 0 */
343		{
344			/* We are testing across two threads */
345			ret = pthread_create(&child_th, NULL, tf, td);
346			if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to create the child thread.");  }
347		}
348
349		/* Wait for the child to terminate */
350		if (do_fork != 0)
351		{
352			/* We were testing across processes */
353			ret = 0;
354			chkpid = waitpid(child_pr, &status, 0);
355			if (chkpid != child_pr)
356			{
357				output("Expected pid: %i. Got %i\n", (int)child_pr, (int)chkpid);
358				UNRESOLVED(errno, "Waitpid failed");
359			}
360			if (WIFSIGNALED(status))
361			{
362				output("Child process killed with signal %d\n",WTERMSIG(status));
363				UNRESOLVED( -1 , "Child process was killed");
364			}
365
366			if (WIFEXITED(status))
367			{
368				ret = WEXITSTATUS(status);
369			}
370			else
371			{
372				UNRESOLVED( -1, "Child process was neither killed nor exited");
373			}
374
375			if (ret != 0)
376			{
377				exit(ret); /* Output has already been closed in child */
378			}
379
380		}
381		else /* child was a thread */
382		{
383			ret = pthread_join(child_th, NULL);
384			if (ret != 0)  {  UNRESOLVED(ret, "[parent] Unable to join the thread");  }
385		}
386
387		/* Check the child status */
388		if (td->status != EBUSY)
389		{
390			output("Unexpected return value: %d (%s)\n", td->status, strerror(td->status));
391			FAILED("pthread_mutex_trylock() did not return EBUSY in the child");
392		}
393
394		/* Unlock the mutex */
395		ret= pthread_mutex_unlock(&(td->mtx));
396		if (ret != 0)  {  UNRESOLVED(ret, "Failed to unlock the mutex");  }
397
398/**********
399 * Destroy the data
400 */
401		ret = pthread_mutex_destroy(&(td->mtx));
402		if (ret != 0)  {  UNRESOLVED(ret, "Failed to destroy the mutex");  }
403
404		ret = pthread_mutexattr_destroy(&ma);
405		if (ret != 0)  {  UNRESOLVED(ret, "Failed to destroy the mutex attribute object");  }
406
407	}  /* Proceed to the next scenario */
408
409	#if VERBOSE > 0
410	output("Test passed\n");
411	#endif
412
413	PASSED;
414}
415
416