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 */ |
40 | static 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 | |
54 | static 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 | |
63 | static 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 | |
106 | static 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 | |
153 | int 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 | |