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 32static const int64_t some_values[] = { 33 0, 34 1, 35 -1, 36 LLONG_MAX, 37 LLONG_MIN, 38}; 39 40static int64_t 41pack_sync_value(xcb_sync_int64_t val) 42{ 43 return ((int64_t)val.hi << 32) | val.lo; 44} 45 46static int64_t 47counter_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 58static xcb_sync_int64_t 59sync_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 */ 72static void 73test_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 */ 98static void 99test_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. */ 124static void 125test_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. */ 154static void 155test_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 224static void 225test_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 254static void 255test_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 mask = XCB_SYNC_CA_TEST_TYPE | XCB_SYNC_CA_DELTA; 264 uint32_t test_type = (some_values[i] >= 0 ? 265 XCB_SYNC_TESTTYPE_POSITIVE_COMPARISON : 266 XCB_SYNC_TESTTYPE_NEGATIVE_COMPARISON); 267 uint32_t values[] = { test_type, some_values[i] >> 32, some_values[i] }; 268 269 xcb_sync_change_alarm(c, alarm, mask, values); 270 queries[i] = xcb_sync_query_alarm_unchecked(c, alarm); 271 } 272 273 for (int i = 0; i < ARRAY_SIZE(some_values); i++) { 274 xcb_sync_query_alarm_reply_t *reply = 275 xcb_sync_query_alarm_reply(c, queries[i], NULL); 276 int64_t value = pack_sync_value(reply->delta); 277 278 if (value != some_values[i]) { 279 fprintf(stderr, "Setting alarm delta to %lld returned %lld\n", 280 (long long)some_values[i], 281 (long long)value); 282 exit(1); 283 } 284 free(reply); 285 } 286} 287 288int main(int argc, char **argv) 289{ 290 int screen; 291 xcb_connection_t *c = xcb_connect(NULL, &screen); 292 const xcb_query_extension_reply_t *ext = xcb_get_extension_data(c, &xcb_sync_id); 293 294 if (!ext->present) { 295 printf("No XSync present\n"); 296 exit(77); 297 } 298 299 test_create_counter(c); 300 test_set_counter(c); 301 test_change_counter_basic(c); 302 test_change_counter_overflow(c); 303 test_change_alarm_value(c); 304 test_change_alarm_delta(c); 305 306 xcb_disconnect(c); 307 exit(0); 308} 309