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