1 /*
2 * Copyright © 2017 Broadcom
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include <assert.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <limits.h>
28 #include <xcb/sync.h>
29
30 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
31
32 static const int64_t some_values[] = {
33 0,
34 1,
35 -1,
36 LLONG_MAX,
37 LLONG_MIN,
38 };
39
40 static int64_t
pack_sync_value(xcb_sync_int64_t val)41 pack_sync_value(xcb_sync_int64_t val)
42 {
43 return ((int64_t)val.hi << 32) | val.lo;
44 }
45
46 static int64_t
counter_value(struct xcb_connection_t * c,xcb_sync_query_counter_cookie_t cookie)47 counter_value(struct xcb_connection_t *c,
48 xcb_sync_query_counter_cookie_t cookie)
49 {
50 xcb_sync_query_counter_reply_t *reply =
51 xcb_sync_query_counter_reply(c, cookie, NULL);
52 int64_t value = pack_sync_value(reply->counter_value);
53
54 free(reply);
55 return value;
56 }
57
58 static xcb_sync_int64_t
sync_value(int64_t value)59 sync_value(int64_t value)
60 {
61 xcb_sync_int64_t v = {
62 .hi = value >> 32,
63 .lo = value,
64 };
65
66 return v;
67 }
68
69 /* Initializes counters with a bunch of interesting values and makes
70 * sure it comes back the same.
71 */
72 static void
test_create_counter(xcb_connection_t * c)73 test_create_counter(xcb_connection_t *c)
74 {
75 xcb_sync_query_counter_cookie_t queries[ARRAY_SIZE(some_values)];
76
77 for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
78 xcb_sync_counter_t counter = xcb_generate_id(c);
79 xcb_sync_create_counter(c, counter, sync_value(some_values[i]));
80 queries[i] = xcb_sync_query_counter_unchecked(c, counter);
81 }
82
83 for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
84 int64_t value = counter_value(c, queries[i]);
85
86 if (value != some_values[i]) {
87 fprintf(stderr, "Creating counter with %lld returned %lld\n",
88 (long long)some_values[i],
89 (long long)value);
90 exit(1);
91 }
92 }
93 }
94
95 /* Set a single counter to a bunch of interesting values and make sure
96 * it comes the same.
97 */
98 static void
test_set_counter(xcb_connection_t * c)99 test_set_counter(xcb_connection_t *c)
100 {
101 xcb_sync_counter_t counter = xcb_generate_id(c);
102 xcb_sync_query_counter_cookie_t queries[ARRAY_SIZE(some_values)];
103
104 xcb_sync_create_counter(c, counter, sync_value(0));
105
106 for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
107 xcb_sync_set_counter(c, counter, sync_value(some_values[i]));
108 queries[i] = xcb_sync_query_counter_unchecked(c, counter);
109 }
110
111 for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
112 int64_t value = counter_value(c, queries[i]);
113
114 if (value != some_values[i]) {
115 fprintf(stderr, "Setting counter to %lld returned %lld\n",
116 (long long)some_values[i],
117 (long long)value);
118 exit(1);
119 }
120 }
121 }
122
123 /* Add [0, 1, 2, 3] to a counter and check that the values stick. */
124 static void
test_change_counter_basic(xcb_connection_t * c)125 test_change_counter_basic(xcb_connection_t *c)
126 {
127 int iterations = 4;
128 xcb_sync_query_counter_cookie_t queries[iterations];
129
130 xcb_sync_counter_t counter = xcb_generate_id(c);
131 xcb_sync_create_counter(c, counter, sync_value(0));
132
133 for (int i = 0; i < iterations; i++) {
134 xcb_sync_change_counter(c, counter, sync_value(i));
135 queries[i] = xcb_sync_query_counter_unchecked(c, counter);
136 }
137
138 int64_t expected_value = 0;
139 for (int i = 0; i < iterations; i++) {
140 expected_value += i;
141 int64_t value = counter_value(c, queries[i]);
142
143 if (value != expected_value) {
144 fprintf(stderr, "Adding %d to counter expected %lld returned %lld\n",
145 i,
146 (long long)expected_value,
147 (long long)value);
148 exit(1);
149 }
150 }
151 }
152
153 /* Test change_counter where we trigger an integer overflow. */
154 static void
test_change_counter_overflow(xcb_connection_t * c)155 test_change_counter_overflow(xcb_connection_t *c)
156 {
157 int iterations = 4;
158 xcb_sync_query_counter_cookie_t queries[iterations];
159 xcb_void_cookie_t changes[iterations];
160 static const struct {
161 int64_t a, b;
162 } overflow_args[] = {
163 { LLONG_MAX, 1 },
164 { LLONG_MAX, LLONG_MAX },
165 { LLONG_MIN, -1 },
166 { LLONG_MIN, LLONG_MIN },
167 };
168
169 xcb_sync_counter_t counter = xcb_generate_id(c);
170 xcb_sync_create_counter(c, counter, sync_value(0));
171
172 for (int i = 0; i < ARRAY_SIZE(overflow_args); i++) {
173 int64_t a = overflow_args[i].a;
174 int64_t b = overflow_args[i].b;
175 xcb_sync_set_counter(c, counter, sync_value(a));
176 changes[i] = xcb_sync_change_counter_checked(c, counter,
177 sync_value(b));
178 queries[i] = xcb_sync_query_counter(c, counter);
179 }
180
181 for (int i = 0; i < ARRAY_SIZE(overflow_args); i++) {
182 int64_t a = overflow_args[i].a;
183 int64_t b = overflow_args[i].b;
184 xcb_sync_query_counter_reply_t *reply =
185 xcb_sync_query_counter_reply(c, queries[i], NULL);
186 int64_t value = (((int64_t)reply->counter_value.hi << 32) |
187 reply->counter_value.lo);
188 int64_t expected_value = a;
189
190 /* The change_counter should have thrown BadValue */
191 xcb_generic_error_t *e = xcb_request_check(c, changes[i]);
192 if (!e) {
193 fprintf(stderr, "(%lld + %lld) failed to return an error\n",
194 (long long)a,
195 (long long)b);
196 exit(1);
197 }
198
199 if (e->error_code != XCB_VALUE) {
200 fprintf(stderr, "(%lld + %lld) returned %d, not BadValue\n",
201 (long long)a,
202 (long long)b,
203 e->error_code);
204 exit(1);
205 }
206
207 /* The change_counter should have had no other effect if it
208 * errored out.
209 */
210 if (value != expected_value) {
211 fprintf(stderr, "(%lld + %lld) expected %lld returned %lld\n",
212 (long long)a,
213 (long long)b,
214 (long long)expected_value,
215 (long long)value);
216 exit(1);
217 }
218
219 free(e);
220 free(reply);
221 }
222 }
223
224 static void
test_change_alarm_value(xcb_connection_t * c)225 test_change_alarm_value(xcb_connection_t *c)
226 {
227 xcb_sync_alarm_t alarm = xcb_generate_id(c);
228 xcb_sync_query_alarm_cookie_t queries[ARRAY_SIZE(some_values)];
229
230 xcb_sync_create_alarm(c, alarm, 0, NULL);
231
232 for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
233 uint32_t values[] = { some_values[i] >> 32, some_values[i] };
234
235 xcb_sync_change_alarm(c, alarm, XCB_SYNC_CA_VALUE, values);
236 queries[i] = xcb_sync_query_alarm_unchecked(c, alarm);
237 }
238
239 for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
240 xcb_sync_query_alarm_reply_t *reply =
241 xcb_sync_query_alarm_reply(c, queries[i], NULL);
242 int64_t value = pack_sync_value(reply->trigger.wait_value);
243
244 if (value != some_values[i]) {
245 fprintf(stderr, "Setting alarm value to %lld returned %lld\n",
246 (long long)some_values[i],
247 (long long)value);
248 exit(1);
249 }
250 free(reply);
251 }
252 }
253
254 static void
test_change_alarm_delta(xcb_connection_t * c)255 test_change_alarm_delta(xcb_connection_t *c)
256 {
257 xcb_sync_alarm_t alarm = xcb_generate_id(c);
258 xcb_sync_query_alarm_cookie_t queries[ARRAY_SIZE(some_values)];
259
260 xcb_sync_create_alarm(c, alarm, 0, NULL);
261
262 for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
263 uint32_t values[] = { some_values[i] >> 32, some_values[i] };
264
265 xcb_sync_change_alarm(c, alarm, XCB_SYNC_CA_DELTA, values);
266 queries[i] = xcb_sync_query_alarm_unchecked(c, alarm);
267 }
268
269 for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
270 xcb_sync_query_alarm_reply_t *reply =
271 xcb_sync_query_alarm_reply(c, queries[i], NULL);
272 int64_t value = pack_sync_value(reply->delta);
273
274 if (value != some_values[i]) {
275 fprintf(stderr, "Setting alarm delta to %lld returned %lld\n",
276 (long long)some_values[i],
277 (long long)value);
278 exit(1);
279 }
280 free(reply);
281 }
282 }
283
main(int argc,char ** argv)284 int main(int argc, char **argv)
285 {
286 int screen;
287 xcb_connection_t *c = xcb_connect(NULL, &screen);
288 const xcb_query_extension_reply_t *ext = xcb_get_extension_data(c, &xcb_sync_id);
289
290 if (!ext->present) {
291 printf("No XSync present\n");
292 exit(77);
293 }
294
295 test_create_counter(c);
296 test_set_counter(c);
297 test_change_counter_basic(c);
298 test_change_counter_overflow(c);
299 test_change_alarm_value(c);
300 test_change_alarm_delta(c);
301
302 xcb_disconnect(c);
303 exit(0);
304 }
305