1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * sync stress test: producer/consumer
3*4882a593Smuzhiyun * Copyright 2015-2016 Collabora Ltd.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Based on the implementation from the Android Open Source Project,
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Copyright 2012 Google, Inc
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * Permission is hereby granted, free of charge, to any person obtaining a
10*4882a593Smuzhiyun * copy of this software and associated documentation files (the "Software"),
11*4882a593Smuzhiyun * to deal in the Software without restriction, including without limitation
12*4882a593Smuzhiyun * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13*4882a593Smuzhiyun * and/or sell copies of the Software, and to permit persons to whom the
14*4882a593Smuzhiyun * Software is furnished to do so, subject to the following conditions:
15*4882a593Smuzhiyun *
16*4882a593Smuzhiyun * The above copyright notice and this permission notice shall be included in
17*4882a593Smuzhiyun * all copies or substantial portions of the Software.
18*4882a593Smuzhiyun *
19*4882a593Smuzhiyun * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20*4882a593Smuzhiyun * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21*4882a593Smuzhiyun * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22*4882a593Smuzhiyun * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23*4882a593Smuzhiyun * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24*4882a593Smuzhiyun * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25*4882a593Smuzhiyun * OTHER DEALINGS IN THE SOFTWARE.
26*4882a593Smuzhiyun */
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun #include <pthread.h>
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #include "sync.h"
31*4882a593Smuzhiyun #include "sw_sync.h"
32*4882a593Smuzhiyun #include "synctest.h"
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun /* IMPORTANT NOTE: if you see this test failing on your system, it may be
35*4882a593Smuzhiyun * due to a shortage of file descriptors. Please ensure your system has
36*4882a593Smuzhiyun * a sensible limit for this test to finish correctly.
37*4882a593Smuzhiyun */
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun /* Returns 1 on error, 0 on success */
busy_wait_on_fence(int fence)40*4882a593Smuzhiyun static int busy_wait_on_fence(int fence)
41*4882a593Smuzhiyun {
42*4882a593Smuzhiyun int error, active;
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun do {
45*4882a593Smuzhiyun error = sync_fence_count_with_status(fence, FENCE_STATUS_ERROR);
46*4882a593Smuzhiyun ASSERT(error == 0, "Error occurred on fence\n");
47*4882a593Smuzhiyun active = sync_fence_count_with_status(fence,
48*4882a593Smuzhiyun FENCE_STATUS_ACTIVE);
49*4882a593Smuzhiyun } while (active);
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun return 0;
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun static struct {
55*4882a593Smuzhiyun int iterations;
56*4882a593Smuzhiyun int threads;
57*4882a593Smuzhiyun int counter;
58*4882a593Smuzhiyun int consumer_timeline;
59*4882a593Smuzhiyun int *producer_timelines;
60*4882a593Smuzhiyun pthread_mutex_t lock;
61*4882a593Smuzhiyun } test_data_mpsc;
62*4882a593Smuzhiyun
mpsc_producer_thread(void * d)63*4882a593Smuzhiyun static int mpsc_producer_thread(void *d)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun int id = (long)d;
66*4882a593Smuzhiyun int fence, valid, i;
67*4882a593Smuzhiyun int *producer_timelines = test_data_mpsc.producer_timelines;
68*4882a593Smuzhiyun int consumer_timeline = test_data_mpsc.consumer_timeline;
69*4882a593Smuzhiyun int iterations = test_data_mpsc.iterations;
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun for (i = 0; i < iterations; i++) {
72*4882a593Smuzhiyun fence = sw_sync_fence_create(consumer_timeline, "fence", i);
73*4882a593Smuzhiyun valid = sw_sync_fence_is_valid(fence);
74*4882a593Smuzhiyun ASSERT(valid, "Failure creating fence\n");
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun /*
77*4882a593Smuzhiyun * Wait for the consumer to finish. Use alternate
78*4882a593Smuzhiyun * means of waiting on the fence
79*4882a593Smuzhiyun */
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun if ((iterations + id) % 8 != 0) {
82*4882a593Smuzhiyun ASSERT(sync_wait(fence, -1) > 0,
83*4882a593Smuzhiyun "Failure waiting on fence\n");
84*4882a593Smuzhiyun } else {
85*4882a593Smuzhiyun ASSERT(busy_wait_on_fence(fence) == 0,
86*4882a593Smuzhiyun "Failure waiting on fence\n");
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun /*
90*4882a593Smuzhiyun * Every producer increments the counter, the consumer
91*4882a593Smuzhiyun * checks and erases it
92*4882a593Smuzhiyun */
93*4882a593Smuzhiyun pthread_mutex_lock(&test_data_mpsc.lock);
94*4882a593Smuzhiyun test_data_mpsc.counter++;
95*4882a593Smuzhiyun pthread_mutex_unlock(&test_data_mpsc.lock);
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun ASSERT(sw_sync_timeline_inc(producer_timelines[id], 1) == 0,
98*4882a593Smuzhiyun "Error advancing producer timeline\n");
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun sw_sync_fence_destroy(fence);
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun return 0;
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun
mpcs_consumer_thread(void)106*4882a593Smuzhiyun static int mpcs_consumer_thread(void)
107*4882a593Smuzhiyun {
108*4882a593Smuzhiyun int fence, merged, tmp, valid, it, i;
109*4882a593Smuzhiyun int *producer_timelines = test_data_mpsc.producer_timelines;
110*4882a593Smuzhiyun int consumer_timeline = test_data_mpsc.consumer_timeline;
111*4882a593Smuzhiyun int iterations = test_data_mpsc.iterations;
112*4882a593Smuzhiyun int n = test_data_mpsc.threads;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun for (it = 1; it <= iterations; it++) {
115*4882a593Smuzhiyun fence = sw_sync_fence_create(producer_timelines[0], "name", it);
116*4882a593Smuzhiyun for (i = 1; i < n; i++) {
117*4882a593Smuzhiyun tmp = sw_sync_fence_create(producer_timelines[i],
118*4882a593Smuzhiyun "name", it);
119*4882a593Smuzhiyun merged = sync_merge("name", tmp, fence);
120*4882a593Smuzhiyun sw_sync_fence_destroy(tmp);
121*4882a593Smuzhiyun sw_sync_fence_destroy(fence);
122*4882a593Smuzhiyun fence = merged;
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun valid = sw_sync_fence_is_valid(fence);
126*4882a593Smuzhiyun ASSERT(valid, "Failure merging fences\n");
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun /*
129*4882a593Smuzhiyun * Make sure we see an increment from every producer thread.
130*4882a593Smuzhiyun * Vary the means by which we wait.
131*4882a593Smuzhiyun */
132*4882a593Smuzhiyun if (iterations % 8 != 0) {
133*4882a593Smuzhiyun ASSERT(sync_wait(fence, -1) > 0,
134*4882a593Smuzhiyun "Producers did not increment as expected\n");
135*4882a593Smuzhiyun } else {
136*4882a593Smuzhiyun ASSERT(busy_wait_on_fence(fence) == 0,
137*4882a593Smuzhiyun "Producers did not increment as expected\n");
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun ASSERT(test_data_mpsc.counter == n * it,
141*4882a593Smuzhiyun "Counter value mismatch!\n");
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun /* Release the producer threads */
144*4882a593Smuzhiyun ASSERT(sw_sync_timeline_inc(consumer_timeline, 1) == 0,
145*4882a593Smuzhiyun "Failure releasing producer threads\n");
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun sw_sync_fence_destroy(fence);
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun return 0;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun
test_consumer_stress_multi_producer_single_consumer(void)153*4882a593Smuzhiyun int test_consumer_stress_multi_producer_single_consumer(void)
154*4882a593Smuzhiyun {
155*4882a593Smuzhiyun int iterations = 1 << 12;
156*4882a593Smuzhiyun int n = 5;
157*4882a593Smuzhiyun long i, ret;
158*4882a593Smuzhiyun int producer_timelines[n];
159*4882a593Smuzhiyun int consumer_timeline;
160*4882a593Smuzhiyun pthread_t threads[n];
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun consumer_timeline = sw_sync_timeline_create();
163*4882a593Smuzhiyun for (i = 0; i < n; i++)
164*4882a593Smuzhiyun producer_timelines[i] = sw_sync_timeline_create();
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun test_data_mpsc.producer_timelines = producer_timelines;
167*4882a593Smuzhiyun test_data_mpsc.consumer_timeline = consumer_timeline;
168*4882a593Smuzhiyun test_data_mpsc.iterations = iterations;
169*4882a593Smuzhiyun test_data_mpsc.threads = n;
170*4882a593Smuzhiyun test_data_mpsc.counter = 0;
171*4882a593Smuzhiyun pthread_mutex_init(&test_data_mpsc.lock, NULL);
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun for (i = 0; i < n; i++) {
174*4882a593Smuzhiyun pthread_create(&threads[i], NULL, (void * (*)(void *))
175*4882a593Smuzhiyun mpsc_producer_thread, (void *)i);
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun /* Consumer thread runs here */
179*4882a593Smuzhiyun ret = mpcs_consumer_thread();
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun for (i = 0; i < n; i++)
182*4882a593Smuzhiyun pthread_join(threads[i], NULL);
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun return ret;
185*4882a593Smuzhiyun }
186