1/*** 2 This file is part of PulseAudio. 3 4 PulseAudio is free software; you can redistribute it and/or modify 5 it under the terms of the GNU Lesser General Public License as published 6 by the Free Software Foundation; either version 2.1 of the License, 7 or (at your option) any later version. 8 9 PulseAudio is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 General Public License for more details. 13 14 You should have received a copy of the GNU Lesser General Public License 15 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 16***/ 17 18#ifdef HAVE_CONFIG_H 19#include <config.h> 20#endif 21 22#ifdef HAVE_PTHREAD 23#include <pthread.h> 24#ifdef HAVE_PTHREAD_SETAFFINITY_NP 25#ifdef __FreeBSD__ 26#include <pthread_np.h> 27#include <sys/param.h> 28#include <sys/cpuset.h> 29#endif 30#endif 31#endif 32 33#include <check.h> 34 35#include <pulsecore/thread.h> 36#include <pulsecore/once.h> 37#include <pulsecore/log.h> 38#include <pulsecore/core-util.h> 39#include <pulsecore/atomic.h> 40#include <pulse/xmalloc.h> 41 42static pa_once once = PA_ONCE_INIT; 43static volatile unsigned n_run = 0; 44static const char * volatile ran_by = NULL; 45#ifdef HAVE_PTHREAD 46static pthread_barrier_t barrier; 47#endif 48static unsigned n_cpu; 49 50#define N_ITERATIONS 500 51#define N_THREADS 100 52 53static void once_func(void) { 54 n_run++; 55 ran_by = (const char*) pa_thread_get_data(pa_thread_self()); 56} 57 58static void thread_func(void *data) { 59#ifdef HAVE_PTHREAD 60 int r; 61 62#ifdef HAVE_PTHREAD_SETAFFINITY_NP 63 static pa_atomic_t i_cpu = PA_ATOMIC_INIT(0); 64#ifdef __FreeBSD__ 65 cpuset_t mask; 66#else 67 cpu_set_t mask; 68#endif 69 70 CPU_ZERO(&mask); 71 CPU_SET((size_t) (pa_atomic_inc(&i_cpu) % n_cpu), &mask); 72 fail_unless(pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) == 0); 73#endif 74 75 pa_log_debug("started up: %s", (char *) data); 76 77 r = pthread_barrier_wait(&barrier); 78 fail_unless(r == 0 || r == PTHREAD_BARRIER_SERIAL_THREAD); 79#endif /* HAVE_PTHREAD */ 80 81 pa_run_once(&once, once_func); 82} 83 84START_TEST (once_test) { 85 unsigned n, i; 86 87 n_cpu = pa_ncpus(); 88 89 for (n = 0; n < N_ITERATIONS; n++) { 90 pa_thread* threads[N_THREADS]; 91 92#ifdef HAVE_PTHREAD 93 fail_unless(pthread_barrier_init(&barrier, NULL, N_THREADS) == 0); 94#endif 95 96 /* Yes, kinda ugly */ 97 pa_zero(once); 98 99 for (i = 0; i < N_THREADS; i++) 100 threads[i] = pa_thread_new("once", thread_func, pa_sprintf_malloc("Thread #%i", i+1)); 101 102 for (i = 0; i < N_THREADS; i++) 103 pa_thread_join(threads[i]); 104 105 fail_unless(n_run == 1); 106 pa_log_info("ran by %s", ran_by); 107 108 for (i = 0; i < N_THREADS; i++) { 109 pa_xfree(pa_thread_get_data(threads[i])); 110 pa_thread_free(threads[i]); 111 } 112 113 n_run = 0; 114 ran_by = NULL; 115 116#ifdef HAVE_PTHREAD 117 fail_unless(pthread_barrier_destroy(&barrier) == 0); 118#endif 119 } 120} 121END_TEST 122 123int main(int argc, char *argv[]) { 124 int failed = 0; 125 Suite *s; 126 TCase *tc; 127 SRunner *sr; 128 129 if (!getenv("MAKE_CHECK")) 130 pa_log_set_level(PA_LOG_DEBUG); 131 132 s = suite_create("Once"); 133 tc = tcase_create("once"); 134 tcase_add_test(tc, once_test); 135 /* the default timeout is too small, 136 * set it to a reasonable large one. 137 */ 138 tcase_set_timeout(tc, 60 * 60); 139 suite_add_tcase(s, tc); 140 141 sr = srunner_create(s); 142 srunner_run_all(sr, CK_NORMAL); 143 failed = srunner_ntests_failed(sr); 144 srunner_free(sr); 145 146 return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 147} 148