Home | History | Annotate | Download | only in futex
      1 /*
      2  * Copyright (C) 2015 Cyril Hrubis <chrubis (at) suse.cz>
      3  *
      4  * Licensed under the GNU GPLv2 or later.
      5  * This program is free software;  you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation; either version 2 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
     13  * the GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program;  if not, write to the Free Software
     17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
     18  */
     19  /*
     20   * Block several threads on a private mutex, then wake them up.
     21   */
     22 
     23 #include <errno.h>
     24 #include <pthread.h>
     25 
     26 #include "test.h"
     27 #include "safe_macros.h"
     28 #include "futextest.h"
     29 #include "futex_utils.h"
     30 
     31 const char *TCID="futex_wake02";
     32 const int TST_TOTAL=11;
     33 
     34 static futex_t futex = FUTEX_INITIALIZER;
     35 
     36 static volatile int threads_flags[55];
     37 
     38 static int threads_awake(void)
     39 {
     40 	int ret = 0;
     41 	unsigned int i;
     42 
     43 	for (i = 0; i < ARRAY_SIZE(threads_flags); i++) {
     44 		if (threads_flags[i])
     45 			ret++;
     46 	}
     47 
     48 	return ret;
     49 }
     50 
     51 static void clear_threads_awake(void)
     52 {
     53 	unsigned int i;
     54 
     55 	for (i = 0; i < ARRAY_SIZE(threads_flags); i++)
     56 		threads_flags[i] = 0;
     57 }
     58 
     59 static void *threaded(void *arg)
     60 {
     61 	long i = (long)arg;
     62 
     63 	futex_wait(&futex, futex, NULL, FUTEX_PRIVATE_FLAG);
     64 
     65 	threads_flags[i] = 1;
     66 
     67 	return NULL;
     68 }
     69 
     70 static void do_child(void)
     71 {
     72 	int res, i, j, awake;
     73 	pthread_t t[55];
     74 
     75 	for (i = 0; i < (int)ARRAY_SIZE(t); i++) {
     76 		res = pthread_create(&t[i], NULL, threaded, (void*)((long)i));
     77 		if (res) {
     78 			tst_brkm(TBROK, NULL, "pthread_create(): %s",
     79 			         tst_strerrno(res));
     80 		}
     81 	}
     82 
     83 	while (wait_for_threads(ARRAY_SIZE(t)))
     84 		usleep(100);
     85 
     86 	for (i = 1; i <= 10; i++) {
     87 		clear_threads_awake();
     88 		res = futex_wake(&futex, i, FUTEX_PRIVATE_FLAG);
     89 		if (i != res) {
     90 			tst_resm(TFAIL,
     91 			         "futex_wake() woken up %i threads, expected %i",
     92 			         res, i);
     93 		}
     94 
     95 		for (j = 0; j < 100000; j++) {
     96 			awake = threads_awake();
     97 			if (awake == i)
     98 				break;
     99 
    100 			usleep(100);
    101 		}
    102 
    103 		if (awake == i) {
    104 			tst_resm(TPASS, "futex_wake() woken up %i threads", i);
    105 		} else {
    106 			tst_resm(TFAIL, "Woken up %i threads, expected %i",
    107 			         awake, i);
    108 		}
    109 	}
    110 
    111 	res = futex_wake(&futex, 1, FUTEX_PRIVATE_FLAG);
    112 
    113 	if (res) {
    114 		tst_resm(TFAIL, "futex_wake() woken up %i, none were waiting",
    115 		         res);
    116 	} else {
    117 		tst_resm(TPASS, "futex_wake() woken up 0 threads");
    118 	}
    119 
    120 	for (i = 0; i < (int)ARRAY_SIZE(t); i++)
    121 		pthread_join(t[i], NULL);
    122 
    123 	tst_exit();
    124 }
    125 
    126 /*
    127  * We do the real test in a child because with the test -i parameter the loop
    128  * that checks that all threads are sleeping may fail with ENOENT. That is
    129  * because some of the threads from previous run may still be there.
    130  *
    131  * Which is because the userspace part of pthread_join() sleeps in a futex on a
    132  * pthread tid which is woken up at the end of the exit_mm(tsk) which is before
    133  * the process is removed from the parent thread_group list. So there is a
    134  * small race window where the readdir() returns the process tid as a directory
    135  * under /proc/$PID/tasks/, but the subsequent open() fails with ENOENT because
    136  * the thread was removed meanwhile.
    137  */
    138 static void verify_futex_wake(void)
    139 {
    140 	int pid;
    141 
    142 	pid = tst_fork();
    143 
    144 	switch (pid) {
    145 	case 0:
    146 		do_child();
    147 	case -1:
    148 		tst_brkm(TBROK | TERRNO, NULL, "fork() failed");
    149 	default:
    150 		tst_record_childstatus(NULL, pid);
    151 	}
    152 }
    153 
    154 int main(int argc, char *argv[])
    155 {
    156 	int lc;
    157 
    158 	tst_parse_opts(argc, argv, NULL, NULL);
    159 
    160 	for (lc = 0; TEST_LOOPING(lc); lc++)
    161 		verify_futex_wake();
    162 
    163 	tst_exit();
    164 }
    165