153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2014 David Henningsson, Canonical Ltd. 553a5a1b3Sopenharmony_ci 653a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 753a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 853a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 953a5a1b3Sopenharmony_ci or (at your option) any later version. 1053a5a1b3Sopenharmony_ci 1153a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1253a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1353a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1453a5a1b3Sopenharmony_ci General Public License for more details. 1553a5a1b3Sopenharmony_ci 1653a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1753a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1853a5a1b3Sopenharmony_ci***/ 1953a5a1b3Sopenharmony_ci 2053a5a1b3Sopenharmony_ci/* This test spawns two threads on distinct cpu-cores that pass a value 2153a5a1b3Sopenharmony_ci * between each other through shared memory protected by pa_atomic_t. 2253a5a1b3Sopenharmony_ci * Thread "left" continuously increments a value and writes its contents to memory. 2353a5a1b3Sopenharmony_ci * Thread "right" continuously reads the value and checks whether it was incremented. 2453a5a1b3Sopenharmony_ci * 2553a5a1b3Sopenharmony_ci * With the pa_atomic_load/pa_atomic_store implementations based on __sync_synchronize, 2653a5a1b3Sopenharmony_ci * this will fail after some time (sometimes 2 seconds, sometimes 8 hours) at least 2753a5a1b3Sopenharmony_ci * on ARM Cortex-A53 and ARM Cortex-A57 systems. 2853a5a1b3Sopenharmony_ci * 2953a5a1b3Sopenharmony_ci * On x86_64, it does not. 3053a5a1b3Sopenharmony_ci * 3153a5a1b3Sopenharmony_ci * The chosen implementation in some way mimics a situation that can also occur 3253a5a1b3Sopenharmony_ci * using memfd srbchannel transport. 3353a5a1b3Sopenharmony_ci * 3453a5a1b3Sopenharmony_ci * NOTE: This is a long-running test, so don't execute in normal test suite. 3553a5a1b3Sopenharmony_ci * 3653a5a1b3Sopenharmony_ci * */ 3753a5a1b3Sopenharmony_ci 3853a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 3953a5a1b3Sopenharmony_ci#include <config.h> 4053a5a1b3Sopenharmony_ci#endif 4153a5a1b3Sopenharmony_ci 4253a5a1b3Sopenharmony_ci#include <unistd.h> 4353a5a1b3Sopenharmony_ci#include <check.h> 4453a5a1b3Sopenharmony_ci 4553a5a1b3Sopenharmony_ci#include <pulsecore/thread.h> 4653a5a1b3Sopenharmony_ci#include <pulse/rtclock.h> 4753a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 4853a5a1b3Sopenharmony_ci#include <pulsecore/semaphore.h> 4953a5a1b3Sopenharmony_ci#include <pthread.h> 5053a5a1b3Sopenharmony_ci#ifdef __FreeBSD__ 5153a5a1b3Sopenharmony_ci#include <pthread_np.h> 5253a5a1b3Sopenharmony_ci#endif 5353a5a1b3Sopenharmony_ci#include <pulsecore/atomic.h> 5453a5a1b3Sopenharmony_ci 5553a5a1b3Sopenharmony_ci#define MEMORY_SIZE (8 * 2 * 1024 * 1024) 5653a5a1b3Sopenharmony_ci 5753a5a1b3Sopenharmony_ci 5853a5a1b3Sopenharmony_citypedef struct io_t { 5953a5a1b3Sopenharmony_ci pa_atomic_t *flag; 6053a5a1b3Sopenharmony_ci char* memory; 6153a5a1b3Sopenharmony_ci#ifdef __FreeBSD__ 6253a5a1b3Sopenharmony_ci cpuset_t cpuset; 6353a5a1b3Sopenharmony_ci#else 6453a5a1b3Sopenharmony_ci cpu_set_t cpuset; 6553a5a1b3Sopenharmony_ci#endif 6653a5a1b3Sopenharmony_ci} io_t; 6753a5a1b3Sopenharmony_ci 6853a5a1b3Sopenharmony_cistatic void read_func(void* data) { 6953a5a1b3Sopenharmony_ci io_t *io = (io_t *) data; 7053a5a1b3Sopenharmony_ci size_t expect = 0; 7153a5a1b3Sopenharmony_ci size_t value = 0; 7253a5a1b3Sopenharmony_ci pthread_setaffinity_np(pthread_self(), sizeof(io->cpuset), &io->cpuset); 7353a5a1b3Sopenharmony_ci while(1) { 7453a5a1b3Sopenharmony_ci if(pa_atomic_load(io->flag) == 1) { 7553a5a1b3Sopenharmony_ci memcpy(&value, io->memory, sizeof(value)); 7653a5a1b3Sopenharmony_ci pa_atomic_sub(io->flag, 1); 7753a5a1b3Sopenharmony_ci ck_assert_uint_eq(value, expect); 7853a5a1b3Sopenharmony_ci ++expect; 7953a5a1b3Sopenharmony_ci } 8053a5a1b3Sopenharmony_ci } 8153a5a1b3Sopenharmony_ci} 8253a5a1b3Sopenharmony_ci 8353a5a1b3Sopenharmony_cistatic void write_func(void* data) { 8453a5a1b3Sopenharmony_ci io_t *io = (io_t *) data; 8553a5a1b3Sopenharmony_ci size_t value = 0; 8653a5a1b3Sopenharmony_ci pthread_setaffinity_np(pthread_self(), sizeof(io->cpuset), &io->cpuset); 8753a5a1b3Sopenharmony_ci while(1) { 8853a5a1b3Sopenharmony_ci if(pa_atomic_load(io->flag) == 0) { 8953a5a1b3Sopenharmony_ci memcpy(io->memory, &value, sizeof(value)); 9053a5a1b3Sopenharmony_ci pa_atomic_add(io->flag, 1); 9153a5a1b3Sopenharmony_ci ++value; 9253a5a1b3Sopenharmony_ci } 9353a5a1b3Sopenharmony_ci } 9453a5a1b3Sopenharmony_ci} 9553a5a1b3Sopenharmony_ci 9653a5a1b3Sopenharmony_ciSTART_TEST (atomic_test) { 9753a5a1b3Sopenharmony_ci pa_thread *thread1, *thread2; 9853a5a1b3Sopenharmony_ci io_t io1, io2; 9953a5a1b3Sopenharmony_ci 10053a5a1b3Sopenharmony_ci char* memory = pa_xmalloc0(MEMORY_SIZE); 10153a5a1b3Sopenharmony_ci pa_atomic_t flag = PA_ATOMIC_INIT(0); 10253a5a1b3Sopenharmony_ci memset(memory, 0, MEMORY_SIZE); 10353a5a1b3Sopenharmony_ci 10453a5a1b3Sopenharmony_ci /* intentionally misalign memory since srbchannel also does not 10553a5a1b3Sopenharmony_ci * always read/write aligned. Might be a red hering. */ 10653a5a1b3Sopenharmony_ci io1.memory = io2.memory = memory + 1025; 10753a5a1b3Sopenharmony_ci io1.flag = io2.flag = &flag; 10853a5a1b3Sopenharmony_ci 10953a5a1b3Sopenharmony_ci CPU_ZERO(&io1.cpuset); 11053a5a1b3Sopenharmony_ci CPU_SET(1, &io1.cpuset); 11153a5a1b3Sopenharmony_ci thread1 = pa_thread_new("left", &write_func, &io1); 11253a5a1b3Sopenharmony_ci 11353a5a1b3Sopenharmony_ci CPU_ZERO(&io2.cpuset); 11453a5a1b3Sopenharmony_ci CPU_SET(3, &io2.cpuset); 11553a5a1b3Sopenharmony_ci thread2 = pa_thread_new("right", &read_func, &io2); 11653a5a1b3Sopenharmony_ci pa_thread_free(thread1); 11753a5a1b3Sopenharmony_ci pa_thread_free(thread2); 11853a5a1b3Sopenharmony_ci pa_xfree(memory); 11953a5a1b3Sopenharmony_ci} 12053a5a1b3Sopenharmony_ciEND_TEST 12153a5a1b3Sopenharmony_ci 12253a5a1b3Sopenharmony_ciint main(int argc, char *argv[]) { 12353a5a1b3Sopenharmony_ci int failed = 0; 12453a5a1b3Sopenharmony_ci Suite *s; 12553a5a1b3Sopenharmony_ci TCase *tc; 12653a5a1b3Sopenharmony_ci SRunner *sr; 12753a5a1b3Sopenharmony_ci 12853a5a1b3Sopenharmony_ci if (!getenv("MAKE_CHECK")) 12953a5a1b3Sopenharmony_ci pa_log_set_level(PA_LOG_DEBUG); 13053a5a1b3Sopenharmony_ci 13153a5a1b3Sopenharmony_ci s = suite_create("atomic"); 13253a5a1b3Sopenharmony_ci tc = tcase_create("atomic"); 13353a5a1b3Sopenharmony_ci tcase_add_test(tc, atomic_test); 13453a5a1b3Sopenharmony_ci suite_add_tcase(s, tc); 13553a5a1b3Sopenharmony_ci 13653a5a1b3Sopenharmony_ci sr = srunner_create(s); 13753a5a1b3Sopenharmony_ci srunner_run_all(sr, CK_NORMAL); 13853a5a1b3Sopenharmony_ci failed = srunner_ntests_failed(sr); 13953a5a1b3Sopenharmony_ci srunner_free(sr); 14053a5a1b3Sopenharmony_ci 14153a5a1b3Sopenharmony_ci return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 14253a5a1b3Sopenharmony_ci} 143