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

source code of linux/tools/testing/selftests/sync/sync_stress_consumer.c