305 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			305 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Copyright © 2017 Broadcom
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a
 | |
|  * copy of this software and associated documentation files (the "Software"),
 | |
|  * to deal in the Software without restriction, including without limitation
 | |
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | |
|  * and/or sell copies of the Software, and to permit persons to whom the
 | |
|  * Software is furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice (including the next
 | |
|  * paragraph) shall be included in all copies or substantial portions of the
 | |
|  * Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | |
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | |
|  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 | |
|  * IN THE SOFTWARE.
 | |
|  */
 | |
| 
 | |
| #include <assert.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <limits.h>
 | |
| #include <xcb/sync.h>
 | |
| 
 | |
| #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 | |
| 
 | |
| static const int64_t some_values[] = {
 | |
|         0,
 | |
|         1,
 | |
|         -1,
 | |
|         LLONG_MAX,
 | |
|         LLONG_MIN,
 | |
| };
 | |
| 
 | |
| static int64_t
 | |
| pack_sync_value(xcb_sync_int64_t val)
 | |
| {
 | |
|     return ((int64_t)val.hi << 32) | val.lo;
 | |
| }
 | |
| 
 | |
| static int64_t
 | |
| counter_value(struct xcb_connection_t *c,
 | |
|               xcb_sync_query_counter_cookie_t cookie)
 | |
| {
 | |
|     xcb_sync_query_counter_reply_t *reply =
 | |
|         xcb_sync_query_counter_reply(c, cookie, NULL);
 | |
|     int64_t value = pack_sync_value(reply->counter_value);
 | |
| 
 | |
|     free(reply);
 | |
|     return value;
 | |
| }
 | |
| 
 | |
| static xcb_sync_int64_t
 | |
| sync_value(int64_t value)
 | |
| {
 | |
|     xcb_sync_int64_t v = {
 | |
|         .hi = value >> 32,
 | |
|         .lo = value,
 | |
|     };
 | |
| 
 | |
|     return v;
 | |
| }
 | |
| 
 | |
| /* Initializes counters with a bunch of interesting values and makes
 | |
|  * sure it comes back the same.
 | |
|  */
 | |
| static void
 | |
| test_create_counter(xcb_connection_t *c)
 | |
| {
 | |
|     xcb_sync_query_counter_cookie_t queries[ARRAY_SIZE(some_values)];
 | |
| 
 | |
|     for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
 | |
|         xcb_sync_counter_t counter = xcb_generate_id(c);
 | |
|         xcb_sync_create_counter(c, counter, sync_value(some_values[i]));
 | |
|         queries[i] = xcb_sync_query_counter_unchecked(c, counter);
 | |
|     }
 | |
| 
 | |
|     for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
 | |
|         int64_t value = counter_value(c, queries[i]);
 | |
| 
 | |
|         if (value != some_values[i]) {
 | |
|             fprintf(stderr, "Creating counter with %lld returned %lld\n",
 | |
|                     (long long)some_values[i],
 | |
|                     (long long)value);
 | |
|             exit(1);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Set a single counter to a bunch of interesting values and make sure
 | |
|  * it comes the same.
 | |
|  */
 | |
| static void
 | |
| test_set_counter(xcb_connection_t *c)
 | |
| {
 | |
|     xcb_sync_counter_t counter = xcb_generate_id(c);
 | |
|     xcb_sync_query_counter_cookie_t queries[ARRAY_SIZE(some_values)];
 | |
| 
 | |
|     xcb_sync_create_counter(c, counter, sync_value(0));
 | |
| 
 | |
|     for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
 | |
|         xcb_sync_set_counter(c, counter, sync_value(some_values[i]));
 | |
|         queries[i] = xcb_sync_query_counter_unchecked(c, counter);
 | |
|     }
 | |
| 
 | |
|     for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
 | |
|         int64_t value = counter_value(c, queries[i]);
 | |
| 
 | |
|         if (value != some_values[i]) {
 | |
|             fprintf(stderr, "Setting counter to %lld returned %lld\n",
 | |
|                     (long long)some_values[i],
 | |
|                     (long long)value);
 | |
|             exit(1);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Add [0, 1, 2, 3] to a counter and check that the values stick. */
 | |
| static void
 | |
| test_change_counter_basic(xcb_connection_t *c)
 | |
| {
 | |
|     int iterations = 4;
 | |
|     xcb_sync_query_counter_cookie_t queries[iterations];
 | |
| 
 | |
|     xcb_sync_counter_t counter = xcb_generate_id(c);
 | |
|     xcb_sync_create_counter(c, counter, sync_value(0));
 | |
| 
 | |
|     for (int i = 0; i < iterations; i++) {
 | |
|         xcb_sync_change_counter(c, counter, sync_value(i));
 | |
|         queries[i] = xcb_sync_query_counter_unchecked(c, counter);
 | |
|     }
 | |
| 
 | |
|     int64_t expected_value = 0;
 | |
|     for (int i = 0; i < iterations; i++) {
 | |
|         expected_value += i;
 | |
|         int64_t value = counter_value(c, queries[i]);
 | |
| 
 | |
|         if (value != expected_value) {
 | |
|             fprintf(stderr, "Adding %d to counter expected %lld returned %lld\n",
 | |
|                     i,
 | |
|                     (long long)expected_value,
 | |
|                     (long long)value);
 | |
|             exit(1);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Test change_counter where we trigger an integer overflow. */
 | |
| static void
 | |
| test_change_counter_overflow(xcb_connection_t *c)
 | |
| {
 | |
|     int iterations = 4;
 | |
|     xcb_sync_query_counter_cookie_t queries[iterations];
 | |
|     xcb_void_cookie_t changes[iterations];
 | |
|     static const struct {
 | |
|         int64_t a, b;
 | |
|     } overflow_args[] = {
 | |
|         { LLONG_MAX, 1 },
 | |
|         { LLONG_MAX, LLONG_MAX },
 | |
|         { LLONG_MIN, -1 },
 | |
|         { LLONG_MIN, LLONG_MIN },
 | |
|     };
 | |
| 
 | |
|     xcb_sync_counter_t counter = xcb_generate_id(c);
 | |
|     xcb_sync_create_counter(c, counter, sync_value(0));
 | |
| 
 | |
|     for (int i = 0; i < ARRAY_SIZE(overflow_args); i++) {
 | |
|         int64_t a = overflow_args[i].a;
 | |
|         int64_t b = overflow_args[i].b;
 | |
|         xcb_sync_set_counter(c, counter, sync_value(a));
 | |
|         changes[i] = xcb_sync_change_counter_checked(c, counter,
 | |
|                                                      sync_value(b));
 | |
|         queries[i] = xcb_sync_query_counter(c, counter);
 | |
|     }
 | |
| 
 | |
|     for (int i = 0; i < ARRAY_SIZE(overflow_args); i++) {
 | |
|         int64_t a = overflow_args[i].a;
 | |
|         int64_t b = overflow_args[i].b;
 | |
|         xcb_sync_query_counter_reply_t *reply =
 | |
|             xcb_sync_query_counter_reply(c, queries[i], NULL);
 | |
|         int64_t value = (((int64_t)reply->counter_value.hi << 32) |
 | |
|                          reply->counter_value.lo);
 | |
|         int64_t expected_value = a;
 | |
| 
 | |
|         /* The change_counter should have thrown BadValue */
 | |
|         xcb_generic_error_t *e = xcb_request_check(c, changes[i]);
 | |
|         if (!e) {
 | |
|             fprintf(stderr, "(%lld + %lld) failed to return an error\n",
 | |
|                     (long long)a,
 | |
|                     (long long)b);
 | |
|             exit(1);
 | |
|         }
 | |
| 
 | |
|         if (e->error_code != XCB_VALUE) {
 | |
|             fprintf(stderr, "(%lld + %lld) returned %d, not BadValue\n",
 | |
|                     (long long)a,
 | |
|                     (long long)b,
 | |
|                     e->error_code);
 | |
|             exit(1);
 | |
|         }
 | |
| 
 | |
|         /* The change_counter should have had no other effect if it
 | |
|          * errored out.
 | |
|          */
 | |
|         if (value != expected_value) {
 | |
|             fprintf(stderr, "(%lld + %lld) expected %lld returned %lld\n",
 | |
|                     (long long)a,
 | |
|                     (long long)b,
 | |
|                     (long long)expected_value,
 | |
|                     (long long)value);
 | |
|             exit(1);
 | |
|         }
 | |
| 
 | |
|         free(e);
 | |
|         free(reply);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| test_change_alarm_value(xcb_connection_t *c)
 | |
| {
 | |
|     xcb_sync_alarm_t alarm = xcb_generate_id(c);
 | |
|     xcb_sync_query_alarm_cookie_t queries[ARRAY_SIZE(some_values)];
 | |
| 
 | |
|     xcb_sync_create_alarm(c, alarm, 0, NULL);
 | |
| 
 | |
|     for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
 | |
|         uint32_t values[] = { some_values[i] >> 32, some_values[i] };
 | |
| 
 | |
|         xcb_sync_change_alarm(c, alarm, XCB_SYNC_CA_VALUE, values);
 | |
|         queries[i] = xcb_sync_query_alarm_unchecked(c, alarm);
 | |
|     }
 | |
| 
 | |
|     for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
 | |
|         xcb_sync_query_alarm_reply_t *reply =
 | |
|             xcb_sync_query_alarm_reply(c, queries[i], NULL);
 | |
|         int64_t value = pack_sync_value(reply->trigger.wait_value);
 | |
| 
 | |
|         if (value != some_values[i]) {
 | |
|             fprintf(stderr, "Setting alarm value to %lld returned %lld\n",
 | |
|                     (long long)some_values[i],
 | |
|                     (long long)value);
 | |
|             exit(1);
 | |
|         }
 | |
|         free(reply);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| test_change_alarm_delta(xcb_connection_t *c)
 | |
| {
 | |
|     xcb_sync_alarm_t alarm = xcb_generate_id(c);
 | |
|     xcb_sync_query_alarm_cookie_t queries[ARRAY_SIZE(some_values)];
 | |
| 
 | |
|     xcb_sync_create_alarm(c, alarm, 0, NULL);
 | |
| 
 | |
|     for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
 | |
|         uint32_t values[] = { some_values[i] >> 32, some_values[i] };
 | |
| 
 | |
|         xcb_sync_change_alarm(c, alarm, XCB_SYNC_CA_DELTA, values);
 | |
|         queries[i] = xcb_sync_query_alarm_unchecked(c, alarm);
 | |
|     }
 | |
| 
 | |
|     for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
 | |
|         xcb_sync_query_alarm_reply_t *reply =
 | |
|             xcb_sync_query_alarm_reply(c, queries[i], NULL);
 | |
|         int64_t value = pack_sync_value(reply->delta);
 | |
| 
 | |
|         if (value != some_values[i]) {
 | |
|             fprintf(stderr, "Setting alarm delta to %lld returned %lld\n",
 | |
|                     (long long)some_values[i],
 | |
|                     (long long)value);
 | |
|             exit(1);
 | |
|         }
 | |
|         free(reply);
 | |
|     }
 | |
| }
 | |
| 
 | |
| int main(int argc, char **argv)
 | |
| {
 | |
|     int screen;
 | |
|     xcb_connection_t *c = xcb_connect(NULL, &screen);
 | |
|     const xcb_query_extension_reply_t *ext = xcb_get_extension_data(c, &xcb_sync_id);
 | |
| 
 | |
|     if (!ext->present) {
 | |
|         printf("No XSync present\n");
 | |
|         exit(77);
 | |
|     }
 | |
| 
 | |
|     test_create_counter(c);
 | |
|     test_set_counter(c);
 | |
|     test_change_counter_basic(c);
 | |
|     test_change_counter_overflow(c);
 | |
|     test_change_alarm_value(c);
 | |
|     test_change_alarm_delta(c);
 | |
| 
 | |
|     xcb_disconnect(c);
 | |
|     exit(0);
 | |
| }
 |