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);
 | 
						|
}
 |