summaryrefslogtreecommitdiffstats
path: root/src/tests/atomic-test.c
blob: eb986e79dfc86aecf009639fb2cad46e4d30bbec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/***
  This file is part of PulseAudio.

  Copyright 2014 David Henningsson, Canonical Ltd.

  PulseAudio is free software; you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published
  by the Free Software Foundation; either version 2.1 of the License,
  or (at your option) any later version.

  PulseAudio is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/

/* This test spawns two threads on distinct cpu-cores that pass a value
 * between each other through shared memory protected by pa_atomic_t.
 * Thread "left" continuously increments a value and writes its contents to memory.
 * Thread "right" continuously reads the value and checks whether it was incremented.
 *
 * With the pa_atomic_load/pa_atomic_store implementations based on __sync_synchronize,
 * this will fail after some time (sometimes 2 seconds, sometimes 8 hours) at least
 * on ARM Cortex-A53 and ARM Cortex-A57 systems.
 *
 * On x86_64, it does not.
 *
 * The chosen implementation in some way mimics a situation that can also occur
 * using memfd srbchannel transport.
 *
 * NOTE: This is a long-running test, so don't execute in normal test suite.
 *
 * */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <unistd.h>
#include <check.h>

#include <pulsecore/thread.h>
#include <pulse/rtclock.h>
#include <pulse/xmalloc.h>
#include <pulsecore/semaphore.h>
#include <pthread.h>
#include <pulsecore/atomic.h>

#define MEMORY_SIZE (8 * 2 * 1024 * 1024)


typedef struct io_t {
   pa_atomic_t *flag;
   char* memory;
   cpu_set_t cpuset;
} io_t;

static void read_func(void* data) {
   io_t *io = (io_t *) data;
   size_t expect = 0;
   size_t value = 0;
   pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &io->cpuset);
   while(1) {
      if(pa_atomic_load(io->flag) == 1) {
         memcpy(&value, io->memory, sizeof(value));
         pa_atomic_sub(io->flag, 1);
         ck_assert_uint_eq(value, expect);
         ++expect;
      }
   }
}

static void write_func(void* data) {
   io_t *io = (io_t *) data;
   size_t value = 0;
   pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &io->cpuset);
   while(1) {
      if(pa_atomic_load(io->flag) == 0) {
         memcpy(io->memory, &value, sizeof(value));
         pa_atomic_add(io->flag, 1);
         ++value;
      }
   }
}

START_TEST (atomic_test) {
    pa_thread *thread1, *thread2;
    io_t io1, io2;

    char* memory = pa_xmalloc0(MEMORY_SIZE);
    pa_atomic_t flag = PA_ATOMIC_INIT(0);
    memset(memory, 0, MEMORY_SIZE);

    /* intentionally misalign memory since srbchannel also does not
     * always read/write aligned. Might be a red hering. */
    io1.memory = io2.memory = memory + 1025;
    io1.flag = io2.flag = &flag;

    CPU_ZERO(&io1.cpuset);
    CPU_SET(1, &io1.cpuset);
    thread1 = pa_thread_new("left", &write_func, &io1);

    CPU_ZERO(&io2.cpuset);
    CPU_SET(3, &io2.cpuset);
    thread2 = pa_thread_new("right", &read_func, &io2);
    pa_thread_free(thread1);
    pa_thread_free(thread2);
    pa_xfree(memory);
}
END_TEST

int main(int argc, char *argv[]) {
    int failed = 0;
    Suite *s;
    TCase *tc;
    SRunner *sr;

    if (!getenv("MAKE_CHECK"))
        pa_log_set_level(PA_LOG_DEBUG);

    s = suite_create("atomic");
    tc = tcase_create("atomic");
    tcase_add_test(tc, atomic_test);
    suite_add_tcase(s, tc);

    sr = srunner_create(s);
    srunner_run_all(sr, CK_NORMAL);
    failed = srunner_ntests_failed(sr);
    srunner_free(sr);

    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}