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