1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2014 David Henningsson, Canonical Ltd. 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as published 8 by the Free Software Foundation; either version 2.1 of the License, 9 or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public License 17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20/* This test spawns two threads on distinct cpu-cores that pass a value 21 * between each other through shared memory protected by pa_atomic_t. 22 * Thread "left" continuously increments a value and writes its contents to memory. 23 * Thread "right" continuously reads the value and checks whether it was incremented. 24 * 25 * With the pa_atomic_load/pa_atomic_store implementations based on __sync_synchronize, 26 * this will fail after some time (sometimes 2 seconds, sometimes 8 hours) at least 27 * on ARM Cortex-A53 and ARM Cortex-A57 systems. 28 * 29 * On x86_64, it does not. 30 * 31 * The chosen implementation in some way mimics a situation that can also occur 32 * using memfd srbchannel transport. 33 * 34 * NOTE: This is a long-running test, so don't execute in normal test suite. 35 * 36 * */ 37 38#ifdef HAVE_CONFIG_H 39#include <config.h> 40#endif 41 42#include <unistd.h> 43#include <check.h> 44 45#include <pulsecore/thread.h> 46#include <pulse/rtclock.h> 47#include <pulse/xmalloc.h> 48#include <pulsecore/semaphore.h> 49#include <pthread.h> 50#ifdef __FreeBSD__ 51#include <pthread_np.h> 52#endif 53#include <pulsecore/atomic.h> 54 55#define MEMORY_SIZE (8 * 2 * 1024 * 1024) 56 57 58typedef struct io_t { 59 pa_atomic_t *flag; 60 char* memory; 61#ifdef __FreeBSD__ 62 cpuset_t cpuset; 63#else 64 cpu_set_t cpuset; 65#endif 66} io_t; 67 68static void read_func(void* data) { 69 io_t *io = (io_t *) data; 70 size_t expect = 0; 71 size_t value = 0; 72 pthread_setaffinity_np(pthread_self(), sizeof(io->cpuset), &io->cpuset); 73 while(1) { 74 if(pa_atomic_load(io->flag) == 1) { 75 memcpy(&value, io->memory, sizeof(value)); 76 pa_atomic_sub(io->flag, 1); 77 ck_assert_uint_eq(value, expect); 78 ++expect; 79 } 80 } 81} 82 83static void write_func(void* data) { 84 io_t *io = (io_t *) data; 85 size_t value = 0; 86 pthread_setaffinity_np(pthread_self(), sizeof(io->cpuset), &io->cpuset); 87 while(1) { 88 if(pa_atomic_load(io->flag) == 0) { 89 memcpy(io->memory, &value, sizeof(value)); 90 pa_atomic_add(io->flag, 1); 91 ++value; 92 } 93 } 94} 95 96START_TEST (atomic_test) { 97 pa_thread *thread1, *thread2; 98 io_t io1, io2; 99 100 char* memory = pa_xmalloc0(MEMORY_SIZE); 101 pa_atomic_t flag = PA_ATOMIC_INIT(0); 102 memset(memory, 0, MEMORY_SIZE); 103 104 /* intentionally misalign memory since srbchannel also does not 105 * always read/write aligned. Might be a red hering. */ 106 io1.memory = io2.memory = memory + 1025; 107 io1.flag = io2.flag = &flag; 108 109 CPU_ZERO(&io1.cpuset); 110 CPU_SET(1, &io1.cpuset); 111 thread1 = pa_thread_new("left", &write_func, &io1); 112 113 CPU_ZERO(&io2.cpuset); 114 CPU_SET(3, &io2.cpuset); 115 thread2 = pa_thread_new("right", &read_func, &io2); 116 pa_thread_free(thread1); 117 pa_thread_free(thread2); 118 pa_xfree(memory); 119} 120END_TEST 121 122int main(int argc, char *argv[]) { 123 int failed = 0; 124 Suite *s; 125 TCase *tc; 126 SRunner *sr; 127 128 if (!getenv("MAKE_CHECK")) 129 pa_log_set_level(PA_LOG_DEBUG); 130 131 s = suite_create("atomic"); 132 tc = tcase_create("atomic"); 133 tcase_add_test(tc, atomic_test); 134 suite_add_tcase(s, tc); 135 136 sr = srunner_create(s); 137 srunner_run_all(sr, CK_NORMAL); 138 failed = srunner_ntests_failed(sr); 139 srunner_free(sr); 140 141 return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 142} 143