Home | History | Annotate | Download | only in pthread_cond_broadcast
      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 file tests the following assertion:
     18  *
     19  * The pthread_cond_broadcast function unblocks all the threads blocked on the
     20  * conditional variable.
     21 
     22  * The steps are:
     23  *  -> Create N threads which will wait on a condition variable
     24  *  -> broadcast the condition
     25  *  -> Every child checks that it owns the mutex (when possible)
     26  *
     27  */
     28 
     29 #define _POSIX_C_SOURCE 200112L
     30 
     31 #ifndef WITHOUT_XOPEN
     32 #define _XOPEN_SOURCE	600
     33 #endif
     34 
     35 #include <pthread.h>
     36 #include <stdarg.h>
     37 #include <stdio.h>
     38 #include <stdlib.h>
     39 #include <unistd.h>
     40 
     41 #include <errno.h>
     42 #include <signal.h>
     43 #include <string.h>
     44 #include <time.h>
     45 #include <sys/mman.h>
     46 #include <sys/wait.h>
     47 
     48 #include "../testfrmw/testfrmw.h"
     49 #include "../testfrmw/testfrmw.c"
     50 
     51 #define UNRESOLVED_KILLALL(error, text) {			\
     52 	if (td->fork) {						\
     53 		int _nch;					\
     54 		for (_nch = 0; _nch < children.nb; _nch++)	\
     55 			kill(children.ch[_nch].p, SIGKILL);	\
     56 	}							\
     57 	UNRESOLVED(error, text);				\
     58 	}
     59 #define FAILED_KILLALL(text, Tchild) {				\
     60 	if (td->fork) {						\
     61 		int _nch;					\
     62 		for (_nch = 0; _nch < children.nb; _nch++)	\
     63 			kill(children.ch[_nch].p, SIGKILL);	\
     64 	}							\
     65 	FAILED(text);						\
     66 	}
     67 
     68 #ifndef VERBOSE
     69 #define VERBOSE 1
     70 #endif
     71 
     72 #define NCHILDREN (20)
     73 
     74 #define TIMEOUT  (120)
     75 
     76 #ifndef WITHOUT_ALTCLK
     77 #define USE_ALTCLK		/* make tests with MONOTONIC CLOCK if supported */
     78 #endif
     79 
     80 #ifdef WITHOUT_XOPEN
     81 /* We define those to avoid compilation errors, but they won't be used */
     82 #define PTHREAD_MUTEX_DEFAULT 0
     83 #define PTHREAD_MUTEX_NORMAL 0
     84 #define PTHREAD_MUTEX_ERRORCHECK 0
     85 #define PTHREAD_MUTEX_RECURSIVE 0
     86 
     87 #endif
     88 
     89 struct _scenar {
     90 	int m_type;
     91 	int mc_pshared;
     92 	int c_clock;
     93 	int fork;
     94 	char *descr;
     95 } scenarii[] = {
     96 	{
     97 	PTHREAD_MUTEX_DEFAULT, 0, 0, 0, "Default mutex"}, {
     98 	PTHREAD_MUTEX_NORMAL, 0, 0, 0, "Normal mutex"}, {
     99 	PTHREAD_MUTEX_ERRORCHECK, 0, 0, 0, "Errorcheck mutex"}, {
    100 	PTHREAD_MUTEX_RECURSIVE, 0, 0, 0, "Recursive mutex"}, {
    101 	PTHREAD_MUTEX_DEFAULT, 1, 0, 0, "PShared default mutex"}, {
    102 	PTHREAD_MUTEX_NORMAL, 1, 0, 0, "Pshared normal mutex"}, {
    103 	PTHREAD_MUTEX_ERRORCHECK, 1, 0, 0, "Pshared errorcheck mutex"}, {
    104 	PTHREAD_MUTEX_RECURSIVE, 1, 0, 0, "Pshared recursive mutex"}, {
    105 	PTHREAD_MUTEX_DEFAULT, 1, 0, 1,
    106 		    "Pshared default mutex across processes"}, {
    107 	PTHREAD_MUTEX_NORMAL, 1, 0, 1, "Pshared normal mutex across processes"},
    108 	{
    109 	PTHREAD_MUTEX_ERRORCHECK, 1, 0, 1,
    110 		    "Pshared errorcheck mutex across processes"}, {
    111 	PTHREAD_MUTEX_RECURSIVE, 1, 0, 1,
    112 		    "Pshared recursive mutex across processes"},
    113 #ifdef USE_ALTCLK
    114 	{
    115 	PTHREAD_MUTEX_DEFAULT, 1, 1, 1,
    116 		    "Pshared default mutex and alt clock condvar across processes"},
    117 	{
    118 	PTHREAD_MUTEX_NORMAL, 1, 1, 1,
    119 		    "Pshared normal mutex and alt clock condvar across processes"},
    120 	{
    121 	PTHREAD_MUTEX_ERRORCHECK, 1, 1, 1,
    122 		    "Pshared errorcheck mutex and alt clock condvar across processes"},
    123 	{
    124 	PTHREAD_MUTEX_RECURSIVE, 1, 1, 1,
    125 		    "Pshared recursive mutex and alt clock condvar across processes"},
    126 	{
    127 	PTHREAD_MUTEX_DEFAULT, 0, 1, 0, "Default mutex and alt clock condvar"},
    128 	{
    129 	PTHREAD_MUTEX_NORMAL, 0, 1, 0, "Normal mutex and alt clock condvar"},
    130 	{
    131 	PTHREAD_MUTEX_ERRORCHECK, 0, 1, 0,
    132 		    "Errorcheck mutex and alt clock condvar"}, {
    133 	PTHREAD_MUTEX_RECURSIVE, 0, 1, 0,
    134 		    "Recursive mutex and alt clock condvar"}, {
    135 	PTHREAD_MUTEX_DEFAULT, 1, 1, 0,
    136 		    "PShared default mutex and alt clock condvar"}, {
    137 	PTHREAD_MUTEX_NORMAL, 1, 1, 0,
    138 		    "Pshared normal mutex and alt clock condvar"}, {
    139 	PTHREAD_MUTEX_ERRORCHECK, 1, 1, 0,
    140 		    "Pshared errorcheck mutex and alt clock condvar"}, {
    141 	PTHREAD_MUTEX_RECURSIVE, 1, 1, 0,
    142 		    "Pshared recursive mutex and alt clock condvar"},
    143 #endif
    144 };
    145 
    146 #define NSCENAR (sizeof(scenarii) / sizeof(scenarii[0]))
    147 
    148 struct testdata {
    149 	int count;
    150 	pthread_cond_t cnd;
    151 	pthread_mutex_t mtx;
    152 	int predicate;
    153 	clockid_t cid;
    154 	int mtype;
    155 	char fork;
    156 } *td;
    157 
    158 struct {
    159 	union {
    160 		pthread_t t;
    161 		pid_t p;
    162 	} ch[NCHILDREN];
    163 	int nb;
    164 } children;
    165 
    166 static void *child(void *arg)
    167 {
    168 	int ret = 0;
    169 	int timed;
    170 	struct timespec ts;
    171 
    172 	/* lock the mutex */
    173 	ret = pthread_mutex_lock(&td->mtx);
    174 	if (ret != 0)
    175 		UNRESOLVED(ret, "Failed to lock mutex in child");
    176 
    177 	/* increment count */
    178 	td->count++;
    179 	timed = td->count & 1;
    180 
    181 	if (timed) {
    182 		ret = clock_gettime(td->cid, &ts);
    183 		if (ret != 0)
    184 			UNRESOLVED(errno, "Unable to read clock");
    185 		ts.tv_sec += TIMEOUT;
    186 	}
    187 
    188 	do {
    189 		/* Wait while the predicate is false */
    190 		if (timed)
    191 			ret = pthread_cond_timedwait(&td->cnd, &td->mtx, &ts);
    192 		else
    193 			ret = pthread_cond_wait(&td->cnd, &td->mtx);
    194 #if VERBOSE > 5
    195 		output("[child] Wokenup timed=%i, Predicate=%i, ret=%i\n",
    196 		       timed, td->predicate, ret);
    197 #endif
    198 	} while ((ret == 0) && (td->predicate == 0));
    199 	if (ret == ETIMEDOUT) {
    200 		FAILED("Timeout occured. This means a cond signal was lost "
    201 		       "-- or parent died");
    202 	}
    203 	if (ret != 0)
    204 		UNRESOLVED(ret, "Failed to wait for the cond");
    205 
    206 	/* Check that we are owning the mutex */
    207 #ifdef WITHOUT_XOPEN
    208 	ret = pthread_mutex_trylock(&(td->mtx));
    209 	if (ret == 0)
    210 		FAILED("The mutex was not owned after return from "
    211 		       "condition waiting");
    212 #else
    213 	if (td->mtype == PTHREAD_MUTEX_RECURSIVE) {
    214 		ret = pthread_mutex_trylock(&(td->mtx));
    215 		if (ret != 0)
    216 			FAILED("Unable to relock recursive mutex: not owning?");
    217 		ret = pthread_mutex_unlock(&(td->mtx));
    218 		if (ret != 0)
    219 			UNRESOLVED(ret, "Failed to unlock the mutex");
    220 	}
    221 	if (td->mtype == PTHREAD_MUTEX_ERRORCHECK) {
    222 		ret = pthread_mutex_lock(&(td->mtx));
    223 		if (ret == 0)
    224 			FAILED("Was able to lock errorcheck mutex: "
    225 			       "the mutex was not acquired once already?");
    226 	}
    227 #endif
    228 
    229 	/* unlock the mutex */
    230 	ret = pthread_mutex_unlock(&td->mtx);
    231 	if (ret != 0)
    232 		UNRESOLVED(ret, "Failed to unlock the mutex.");
    233 
    234 	return NULL;
    235 }
    236 
    237 static void *timer(void *arg)
    238 {
    239 	unsigned int to = TIMEOUT;
    240 	do {
    241 		to = sleep(to);
    242 	} while (to > 0);
    243 	FAILED_KILLALL("Operation timed out. A signal was lost.", pchildren);
    244 	return NULL;
    245 }
    246 
    247 int main(void)
    248 {
    249 	int ret;
    250 
    251 	pthread_mutexattr_t ma;
    252 	pthread_condattr_t ca;
    253 
    254 	int scenar;
    255 	long pshared, monotonic, cs, mf;
    256 
    257 	int child_count;
    258 
    259 	pid_t pid;
    260 	int status;
    261 
    262 	pthread_t t_timer;
    263 
    264 	pthread_attr_t ta;
    265 
    266 	struct testdata alternativ;
    267 
    268 	output_init();
    269 
    270 	/* check the system abilities */
    271 	pshared = sysconf(_SC_THREAD_PROCESS_SHARED);
    272 	cs = sysconf(_SC_CLOCK_SELECTION);
    273 	monotonic = sysconf(_SC_MONOTONIC_CLOCK);
    274 	mf = sysconf(_SC_MAPPED_FILES);
    275 
    276 #if VERBOSE > 0
    277 	output("Test starting\n");
    278 	output("System abilities:\n");
    279 	output(" TPS : %li\n", pshared);
    280 	output(" CS  : %li\n", cs);
    281 	output(" MON : %li\n", monotonic);
    282 	output(" MF  : %li\n", mf);
    283 	if ((mf < 0) || (pshared < 0))
    284 		output("Process-shared attributes won't be tested\n");
    285 	if ((cs < 0) || (monotonic < 0))
    286 		output("Alternative clock won't be tested\n");
    287 #endif
    288 
    289 	if (monotonic < 0)
    290 		cs = -1;
    291 
    292 #ifndef USE_ALTCLK
    293 	if (cs > 0)
    294 		output("Implementation supports the MONOTONIC CLOCK "
    295 		       "but option is disabled in test.\n");
    296 #endif
    297 
    298 	if (mf < 0) {
    299 		/* Cannot mmap a file, we use an alternative method */
    300 		td = &alternativ;
    301 		pshared = -1;	/* We won't do this testing anyway */
    302 #if VERBOSE > 0
    303 		output("Testdata allocated in the process memory.\n");
    304 #endif
    305 	} else {
    306 		char filename[] = "/tmp/cond_broadcast-XXXXXX";
    307 		size_t sz, ps;
    308 		void *mmaped;
    309 		int fd;
    310 		char *tmp;
    311 
    312 		fd = mkstemp(filename);
    313 		if (fd == -1)
    314 			UNRESOLVED(errno,
    315 				   "Temporary file could not be created");
    316 
    317 		unlink(filename);
    318 
    319 #if VERBOSE > 1
    320 		output("Temp file created (%s).\n", filename);
    321 #endif
    322 
    323 		ps = (size_t) sysconf(_SC_PAGESIZE);
    324 		sz = ((sizeof(struct testdata) / ps) + 1) * ps;
    325 
    326 		tmp = calloc(1, sz);
    327 		if (tmp == NULL)
    328 			UNRESOLVED(errno, "Memory allocation failed");
    329 
    330 		if (write(fd, tmp, sz) != (ssize_t) sz)
    331 			UNRESOLVED(sz, "Writting to the file failed");
    332 
    333 		free(tmp);
    334 
    335 		mmaped =
    336 		    mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    337 		if (mmaped == MAP_FAILED)
    338 			UNRESOLVED(errno, "mmap failed");
    339 
    340 		td = (struct testdata *)mmaped;
    341 
    342 		/* Our datatest structure is now in shared memory */
    343 #if VERBOSE > 1
    344 		output("Testdata allocated in shared memory (%ib).\n",
    345 		       sizeof(struct testdata));
    346 #endif
    347 	}
    348 
    349 	ret = pthread_attr_init(&ta);
    350 	if (ret != 0)
    351 		UNRESOLVED(ret, "[parent] Failed to initialize a thread "
    352 			   "attribute object");
    353 	ret = pthread_attr_setstacksize(&ta, sysconf(_SC_THREAD_STACK_MIN));
    354 	if (ret != 0)
    355 		UNRESOLVED(ret, "[parent] Failed to set thread stack size");
    356 
    357 	/* Do the test for each test scenario */
    358 	for (scenar = 0; scenar < NSCENAR; scenar++) {
    359 		/* set / reset everything */
    360 		td->fork = 0;
    361 		ret = pthread_mutexattr_init(&ma);
    362 		if (ret != 0)
    363 			UNRESOLVED(ret, "[parent] Unable to initialize the "
    364 				   "mutex attribute object");
    365 		ret = pthread_condattr_init(&ca);
    366 		if (ret != 0)
    367 			UNRESOLVED(ret, "[parent] Unable to initialize the "
    368 				   "cond attribute object");
    369 
    370 #ifndef WITHOUT_XOPEN
    371 		/* Set the mutex type */
    372 		ret = pthread_mutexattr_settype(&ma, scenarii[scenar].m_type);
    373 		if (ret != 0)
    374 			UNRESOLVED(ret, "[parent] Unable to set mutex type");
    375 #endif
    376 
    377 		td->mtype = scenarii[scenar].m_type;
    378 
    379 		/* Set the pshared attributes, if supported */
    380 		if ((pshared > 0) && (scenarii[scenar].mc_pshared != 0)) {
    381 			ret = pthread_mutexattr_setpshared(&ma,
    382 							   PTHREAD_PROCESS_SHARED);
    383 			if (ret != 0)
    384 				UNRESOLVED(ret, "[parent] Unable to set "
    385 					   "the mutex process-shared");
    386 			ret = pthread_condattr_setpshared(&ca,
    387 							  PTHREAD_PROCESS_SHARED);
    388 			if (ret != 0)
    389 				UNRESOLVED(ret, "[parent] Unable to set "
    390 					   "the cond var process-shared");
    391 		}
    392 
    393 		/* Set the alternative clock, if supported */
    394 #ifdef USE_ALTCLK
    395 		if ((cs > 0) && (scenarii[scenar].c_clock != 0)) {
    396 			ret = pthread_condattr_setclock(&ca, CLOCK_MONOTONIC);
    397 			if (ret != 0)
    398 				UNRESOLVED(ret, "[parent] Unable to set the "
    399 					   "monotonic clock for the cond");
    400 		}
    401 		ret = pthread_condattr_getclock(&ca, &td->cid);
    402 		if (ret != 0)
    403 			UNRESOLVED(ret, "Unable to get clock from cond attr");
    404 #else
    405 		td->cid = CLOCK_REALTIME;
    406 #endif
    407 
    408 		/* Tell whether the test will be across processes */
    409 		if ((pshared > 0) && (scenarii[scenar].fork != 0))
    410 			td->fork = 1;
    411 
    412 		ret = pthread_cond_init(&td->cnd, &ca);
    413 		if (ret != 0)
    414 			UNRESOLVED(ret, "Cond init failed");
    415 
    416 		ret = pthread_mutex_init(&td->mtx, &ma);
    417 		if (ret != 0)
    418 			UNRESOLVED(ret, "Mutex init failed");
    419 
    420 		ret = pthread_condattr_destroy(&ca);
    421 		if (ret != 0)
    422 			UNRESOLVED(ret, "Failed to destroy the cond var "
    423 				   "attribute object");
    424 
    425 		ret = pthread_mutexattr_destroy(&ma);
    426 		if (ret != 0)
    427 			UNRESOLVED(ret, "Failed to destroy the mutex "
    428 				   "attribute object");
    429 
    430 #if VERBOSE > 2
    431 		output("[parent] Starting test %s\n", scenarii[scenar].descr);
    432 #endif
    433 
    434 		td->count = 0;
    435 
    436 		/* Create all the children */
    437 		for (children.nb = 0; children.nb < NCHILDREN; children.nb++) {
    438 			if (td->fork == 0) {
    439 				ret =
    440 				    pthread_create(&
    441 						   (children.ch[children.nb].t),
    442 						   &ta, child, NULL);
    443 				if (ret != 0)
    444 					UNRESOLVED(ret, "Failed to create "
    445 						   "enough threads");
    446 			} else {
    447 				children.ch[children.nb].p = fork();
    448 				if (children.ch[children.nb].p == 0) {
    449 					child(NULL);
    450 					exit(0);
    451 				}
    452 				if (children.ch[children.nb].p == -1) {
    453 					children.nb--;
    454 					UNRESOLVED_KILLALL(errno, "Failed to "
    455 							   "create enough processes");
    456 				}
    457 			}
    458 		}
    459 #if VERBOSE > 4
    460 		output("[parent] Created %i children\n", NCHILDREN);
    461 #endif
    462 
    463 		/* Make sure all children are waiting */
    464 		ret = pthread_mutex_lock(&td->mtx);
    465 		if (ret != 0)
    466 			UNRESOLVED_KILLALL(ret, "Failed to lock mutex");
    467 		child_count = td->count;
    468 		while (child_count < NCHILDREN) {
    469 			ret = pthread_mutex_unlock(&td->mtx);
    470 			if (ret != 0)
    471 				UNRESOLVED_KILLALL(ret,
    472 						   "Failed to unlock mutex");
    473 			sched_yield();
    474 			ret = pthread_mutex_lock(&td->mtx);
    475 			if (ret != 0)
    476 				UNRESOLVED_KILLALL(ret, "Failed to lock mutex");
    477 			child_count = td->count;
    478 		}
    479 
    480 #if VERBOSE > 4
    481 		output("[parent] All children are waiting\n");
    482 #endif
    483 
    484 		ret = pthread_create(&t_timer, NULL, timer, NULL);
    485 		if (ret != 0)
    486 			UNRESOLVED_KILLALL(ret,
    487 					   "Unable to create timer thread");
    488 
    489 		/* Wakeup the children */
    490 		td->predicate = 1;
    491 		ret = pthread_cond_broadcast(&td->cnd);
    492 		if (ret != 0)
    493 			UNRESOLVED_KILLALL(ret, "Failed to broadcast "
    494 					   "the condition.");
    495 
    496 #if VERBOSE > 4
    497 		output("[parent] Condition was signaled\n");
    498 #endif
    499 
    500 		ret = pthread_mutex_unlock(&td->mtx);
    501 		if (ret != 0)
    502 			UNRESOLVED_KILLALL(ret, "Failed to unlock mutex");
    503 
    504 #if VERBOSE > 4
    505 		output("[parent] Joining the children\n");
    506 #endif
    507 
    508 		/* join the children */
    509 		for (--children.nb; children.nb >= 0; children.nb--) {
    510 			if (td->fork == 0) {
    511 				ret = pthread_join(children.ch[children.nb].t,
    512 						   NULL);
    513 				if (ret != 0)
    514 					UNRESOLVED(ret,
    515 						   "Failed to join a child thread");
    516 			} else {
    517 				pid = waitpid(children.ch[children.nb].p,
    518 					      &status, 0);
    519 				if (pid != children.ch[children.nb].p) {
    520 					ret = errno;
    521 					output("Waitpid failed (expected: %i, "
    522 					       "got: %i)\n",
    523 					       children.ch[children.nb].p, pid);
    524 					UNRESOLVED_KILLALL(ret,
    525 							   "Waitpid failed");
    526 				}
    527 				if (WIFEXITED(status)) {
    528 					if (ret != PTS_FAIL)
    529 						ret |= WEXITSTATUS(status);
    530 				}
    531 			}
    532 		}
    533 		if (ret != 0) {
    534 			output_fini();
    535 			exit(ret);
    536 		}
    537 #if VERBOSE > 4
    538 		output("[parent] All children terminated\n");
    539 #endif
    540 
    541 		ret = pthread_cancel(t_timer);
    542 		if (ret != 0)
    543 			UNRESOLVED(ret, "Failed to cancel the timeout handler");
    544 
    545 		ret = pthread_join(t_timer, NULL);
    546 		if (ret != 0)
    547 			UNRESOLVED(ret, "Failed to join the timeout handler");
    548 
    549 		ret = pthread_cond_destroy(&td->cnd);
    550 		if (ret != 0)
    551 			UNRESOLVED(ret, "Failed to destroy the condvar");
    552 
    553 		ret = pthread_mutex_destroy(&td->mtx);
    554 		if (ret != 0)
    555 			UNRESOLVED(ret, "Failed to destroy the mutex");
    556 
    557 	}
    558 
    559 	ret = pthread_attr_destroy(&ta);
    560 	if (ret != 0)
    561 		UNRESOLVED(ret, "Final thread attr destroy failed");
    562 
    563 	PASSED;
    564 }
    565