11b5d61b8Smrg/*
21b5d61b8Smrg * Copyright © 2017 Broadcom
31b5d61b8Smrg *
41b5d61b8Smrg * Permission is hereby granted, free of charge, to any person obtaining a
51b5d61b8Smrg * copy of this software and associated documentation files (the "Software"),
61b5d61b8Smrg * to deal in the Software without restriction, including without limitation
71b5d61b8Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
81b5d61b8Smrg * and/or sell copies of the Software, and to permit persons to whom the
91b5d61b8Smrg * Software is furnished to do so, subject to the following conditions:
101b5d61b8Smrg *
111b5d61b8Smrg * The above copyright notice and this permission notice (including the next
121b5d61b8Smrg * paragraph) shall be included in all copies or substantial portions of the
131b5d61b8Smrg * Software.
141b5d61b8Smrg *
151b5d61b8Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
161b5d61b8Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
171b5d61b8Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
181b5d61b8Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
191b5d61b8Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
201b5d61b8Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
211b5d61b8Smrg * IN THE SOFTWARE.
221b5d61b8Smrg */
231b5d61b8Smrg
241b5d61b8Smrg#include <assert.h>
251b5d61b8Smrg#include <stdio.h>
261b5d61b8Smrg#include <stdlib.h>
271b5d61b8Smrg#include <limits.h>
281b5d61b8Smrg#include <xcb/sync.h>
291b5d61b8Smrg
301b5d61b8Smrg#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
311b5d61b8Smrg
321b5d61b8Smrgstatic const int64_t some_values[] = {
331b5d61b8Smrg        0,
341b5d61b8Smrg        1,
351b5d61b8Smrg        -1,
361b5d61b8Smrg        LLONG_MAX,
371b5d61b8Smrg        LLONG_MIN,
381b5d61b8Smrg};
391b5d61b8Smrg
401b5d61b8Smrgstatic int64_t
411b5d61b8Smrgpack_sync_value(xcb_sync_int64_t val)
421b5d61b8Smrg{
431b5d61b8Smrg    return ((int64_t)val.hi << 32) | val.lo;
441b5d61b8Smrg}
451b5d61b8Smrg
461b5d61b8Smrgstatic int64_t
471b5d61b8Smrgcounter_value(struct xcb_connection_t *c,
481b5d61b8Smrg              xcb_sync_query_counter_cookie_t cookie)
491b5d61b8Smrg{
501b5d61b8Smrg    xcb_sync_query_counter_reply_t *reply =
511b5d61b8Smrg        xcb_sync_query_counter_reply(c, cookie, NULL);
521b5d61b8Smrg    int64_t value = pack_sync_value(reply->counter_value);
531b5d61b8Smrg
541b5d61b8Smrg    free(reply);
551b5d61b8Smrg    return value;
561b5d61b8Smrg}
571b5d61b8Smrg
581b5d61b8Smrgstatic xcb_sync_int64_t
591b5d61b8Smrgsync_value(int64_t value)
601b5d61b8Smrg{
611b5d61b8Smrg    xcb_sync_int64_t v = {
621b5d61b8Smrg        .hi = value >> 32,
631b5d61b8Smrg        .lo = value,
641b5d61b8Smrg    };
651b5d61b8Smrg
661b5d61b8Smrg    return v;
671b5d61b8Smrg}
681b5d61b8Smrg
691b5d61b8Smrg/* Initializes counters with a bunch of interesting values and makes
701b5d61b8Smrg * sure it comes back the same.
711b5d61b8Smrg */
721b5d61b8Smrgstatic void
731b5d61b8Smrgtest_create_counter(xcb_connection_t *c)
741b5d61b8Smrg{
751b5d61b8Smrg    xcb_sync_query_counter_cookie_t queries[ARRAY_SIZE(some_values)];
761b5d61b8Smrg
771b5d61b8Smrg    for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
781b5d61b8Smrg        xcb_sync_counter_t counter = xcb_generate_id(c);
791b5d61b8Smrg        xcb_sync_create_counter(c, counter, sync_value(some_values[i]));
801b5d61b8Smrg        queries[i] = xcb_sync_query_counter_unchecked(c, counter);
811b5d61b8Smrg    }
821b5d61b8Smrg
831b5d61b8Smrg    for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
841b5d61b8Smrg        int64_t value = counter_value(c, queries[i]);
851b5d61b8Smrg
861b5d61b8Smrg        if (value != some_values[i]) {
871b5d61b8Smrg            fprintf(stderr, "Creating counter with %lld returned %lld\n",
881b5d61b8Smrg                    (long long)some_values[i],
891b5d61b8Smrg                    (long long)value);
901b5d61b8Smrg            exit(1);
911b5d61b8Smrg        }
921b5d61b8Smrg    }
931b5d61b8Smrg}
941b5d61b8Smrg
951b5d61b8Smrg/* Set a single counter to a bunch of interesting values and make sure
961b5d61b8Smrg * it comes the same.
971b5d61b8Smrg */
981b5d61b8Smrgstatic void
991b5d61b8Smrgtest_set_counter(xcb_connection_t *c)
1001b5d61b8Smrg{
1011b5d61b8Smrg    xcb_sync_counter_t counter = xcb_generate_id(c);
1021b5d61b8Smrg    xcb_sync_query_counter_cookie_t queries[ARRAY_SIZE(some_values)];
1031b5d61b8Smrg
1041b5d61b8Smrg    xcb_sync_create_counter(c, counter, sync_value(0));
1051b5d61b8Smrg
1061b5d61b8Smrg    for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
1071b5d61b8Smrg        xcb_sync_set_counter(c, counter, sync_value(some_values[i]));
1081b5d61b8Smrg        queries[i] = xcb_sync_query_counter_unchecked(c, counter);
1091b5d61b8Smrg    }
1101b5d61b8Smrg
1111b5d61b8Smrg    for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
1121b5d61b8Smrg        int64_t value = counter_value(c, queries[i]);
1131b5d61b8Smrg
1141b5d61b8Smrg        if (value != some_values[i]) {
1151b5d61b8Smrg            fprintf(stderr, "Setting counter to %lld returned %lld\n",
1161b5d61b8Smrg                    (long long)some_values[i],
1171b5d61b8Smrg                    (long long)value);
1181b5d61b8Smrg            exit(1);
1191b5d61b8Smrg        }
1201b5d61b8Smrg    }
1211b5d61b8Smrg}
1221b5d61b8Smrg
1231b5d61b8Smrg/* Add [0, 1, 2, 3] to a counter and check that the values stick. */
1241b5d61b8Smrgstatic void
1251b5d61b8Smrgtest_change_counter_basic(xcb_connection_t *c)
1261b5d61b8Smrg{
1271b5d61b8Smrg    int iterations = 4;
1281b5d61b8Smrg    xcb_sync_query_counter_cookie_t queries[iterations];
1291b5d61b8Smrg
1301b5d61b8Smrg    xcb_sync_counter_t counter = xcb_generate_id(c);
1311b5d61b8Smrg    xcb_sync_create_counter(c, counter, sync_value(0));
1321b5d61b8Smrg
1331b5d61b8Smrg    for (int i = 0; i < iterations; i++) {
1341b5d61b8Smrg        xcb_sync_change_counter(c, counter, sync_value(i));
1351b5d61b8Smrg        queries[i] = xcb_sync_query_counter_unchecked(c, counter);
1361b5d61b8Smrg    }
1371b5d61b8Smrg
1381b5d61b8Smrg    int64_t expected_value = 0;
1391b5d61b8Smrg    for (int i = 0; i < iterations; i++) {
1401b5d61b8Smrg        expected_value += i;
1411b5d61b8Smrg        int64_t value = counter_value(c, queries[i]);
1421b5d61b8Smrg
1431b5d61b8Smrg        if (value != expected_value) {
1441b5d61b8Smrg            fprintf(stderr, "Adding %d to counter expected %lld returned %lld\n",
1451b5d61b8Smrg                    i,
1461b5d61b8Smrg                    (long long)expected_value,
1471b5d61b8Smrg                    (long long)value);
1481b5d61b8Smrg            exit(1);
1491b5d61b8Smrg        }
1501b5d61b8Smrg    }
1511b5d61b8Smrg}
1521b5d61b8Smrg
1531b5d61b8Smrg/* Test change_counter where we trigger an integer overflow. */
1541b5d61b8Smrgstatic void
1551b5d61b8Smrgtest_change_counter_overflow(xcb_connection_t *c)
1561b5d61b8Smrg{
1571b5d61b8Smrg    int iterations = 4;
1581b5d61b8Smrg    xcb_sync_query_counter_cookie_t queries[iterations];
1591b5d61b8Smrg    xcb_void_cookie_t changes[iterations];
1601b5d61b8Smrg    static const struct {
1611b5d61b8Smrg        int64_t a, b;
1621b5d61b8Smrg    } overflow_args[] = {
1631b5d61b8Smrg        { LLONG_MAX, 1 },
1641b5d61b8Smrg        { LLONG_MAX, LLONG_MAX },
1651b5d61b8Smrg        { LLONG_MIN, -1 },
1661b5d61b8Smrg        { LLONG_MIN, LLONG_MIN },
1671b5d61b8Smrg    };
1681b5d61b8Smrg
1691b5d61b8Smrg    xcb_sync_counter_t counter = xcb_generate_id(c);
1701b5d61b8Smrg    xcb_sync_create_counter(c, counter, sync_value(0));
1711b5d61b8Smrg
1721b5d61b8Smrg    for (int i = 0; i < ARRAY_SIZE(overflow_args); i++) {
1731b5d61b8Smrg        int64_t a = overflow_args[i].a;
1741b5d61b8Smrg        int64_t b = overflow_args[i].b;
1751b5d61b8Smrg        xcb_sync_set_counter(c, counter, sync_value(a));
1761b5d61b8Smrg        changes[i] = xcb_sync_change_counter_checked(c, counter,
1771b5d61b8Smrg                                                     sync_value(b));
1781b5d61b8Smrg        queries[i] = xcb_sync_query_counter(c, counter);
1791b5d61b8Smrg    }
1801b5d61b8Smrg
1811b5d61b8Smrg    for (int i = 0; i < ARRAY_SIZE(overflow_args); i++) {
1821b5d61b8Smrg        int64_t a = overflow_args[i].a;
1831b5d61b8Smrg        int64_t b = overflow_args[i].b;
1841b5d61b8Smrg        xcb_sync_query_counter_reply_t *reply =
1851b5d61b8Smrg            xcb_sync_query_counter_reply(c, queries[i], NULL);
1861b5d61b8Smrg        int64_t value = (((int64_t)reply->counter_value.hi << 32) |
1871b5d61b8Smrg                         reply->counter_value.lo);
1881b5d61b8Smrg        int64_t expected_value = a;
1891b5d61b8Smrg
1901b5d61b8Smrg        /* The change_counter should have thrown BadValue */
1911b5d61b8Smrg        xcb_generic_error_t *e = xcb_request_check(c, changes[i]);
1921b5d61b8Smrg        if (!e) {
1931b5d61b8Smrg            fprintf(stderr, "(%lld + %lld) failed to return an error\n",
1941b5d61b8Smrg                    (long long)a,
1951b5d61b8Smrg                    (long long)b);
1961b5d61b8Smrg            exit(1);
1971b5d61b8Smrg        }
1981b5d61b8Smrg
1991b5d61b8Smrg        if (e->error_code != XCB_VALUE) {
2001b5d61b8Smrg            fprintf(stderr, "(%lld + %lld) returned %d, not BadValue\n",
2011b5d61b8Smrg                    (long long)a,
2021b5d61b8Smrg                    (long long)b,
2031b5d61b8Smrg                    e->error_code);
2041b5d61b8Smrg            exit(1);
2051b5d61b8Smrg        }
2061b5d61b8Smrg
2071b5d61b8Smrg        /* The change_counter should have had no other effect if it
2081b5d61b8Smrg         * errored out.
2091b5d61b8Smrg         */
2101b5d61b8Smrg        if (value != expected_value) {
2111b5d61b8Smrg            fprintf(stderr, "(%lld + %lld) expected %lld returned %lld\n",
2121b5d61b8Smrg                    (long long)a,
2131b5d61b8Smrg                    (long long)b,
2141b5d61b8Smrg                    (long long)expected_value,
2151b5d61b8Smrg                    (long long)value);
2161b5d61b8Smrg            exit(1);
2171b5d61b8Smrg        }
2181b5d61b8Smrg
2191b5d61b8Smrg        free(e);
2201b5d61b8Smrg        free(reply);
2211b5d61b8Smrg    }
2221b5d61b8Smrg}
2231b5d61b8Smrg
2241b5d61b8Smrgstatic void
2251b5d61b8Smrgtest_change_alarm_value(xcb_connection_t *c)
2261b5d61b8Smrg{
2271b5d61b8Smrg    xcb_sync_alarm_t alarm = xcb_generate_id(c);
2281b5d61b8Smrg    xcb_sync_query_alarm_cookie_t queries[ARRAY_SIZE(some_values)];
2291b5d61b8Smrg
2301b5d61b8Smrg    xcb_sync_create_alarm(c, alarm, 0, NULL);
2311b5d61b8Smrg
2321b5d61b8Smrg    for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
2331b5d61b8Smrg        uint32_t values[] = { some_values[i] >> 32, some_values[i] };
2341b5d61b8Smrg
2351b5d61b8Smrg        xcb_sync_change_alarm(c, alarm, XCB_SYNC_CA_VALUE, values);
2361b5d61b8Smrg        queries[i] = xcb_sync_query_alarm_unchecked(c, alarm);
2371b5d61b8Smrg    }
2381b5d61b8Smrg
2391b5d61b8Smrg    for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
2401b5d61b8Smrg        xcb_sync_query_alarm_reply_t *reply =
2411b5d61b8Smrg            xcb_sync_query_alarm_reply(c, queries[i], NULL);
2421b5d61b8Smrg        int64_t value = pack_sync_value(reply->trigger.wait_value);
2431b5d61b8Smrg
2441b5d61b8Smrg        if (value != some_values[i]) {
2451b5d61b8Smrg            fprintf(stderr, "Setting alarm value to %lld returned %lld\n",
2461b5d61b8Smrg                    (long long)some_values[i],
2471b5d61b8Smrg                    (long long)value);
2481b5d61b8Smrg            exit(1);
2491b5d61b8Smrg        }
2501b5d61b8Smrg        free(reply);
2511b5d61b8Smrg    }
2521b5d61b8Smrg}
2531b5d61b8Smrg
2541b5d61b8Smrgstatic void
2551b5d61b8Smrgtest_change_alarm_delta(xcb_connection_t *c)
2561b5d61b8Smrg{
2571b5d61b8Smrg    xcb_sync_alarm_t alarm = xcb_generate_id(c);
2581b5d61b8Smrg    xcb_sync_query_alarm_cookie_t queries[ARRAY_SIZE(some_values)];
2591b5d61b8Smrg
2601b5d61b8Smrg    xcb_sync_create_alarm(c, alarm, 0, NULL);
2611b5d61b8Smrg
2621b5d61b8Smrg    for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
263f2346221Smrg        uint32_t mask = XCB_SYNC_CA_TEST_TYPE | XCB_SYNC_CA_DELTA;
264f2346221Smrg        uint32_t test_type = (some_values[i] >= 0 ?
265f2346221Smrg                               XCB_SYNC_TESTTYPE_POSITIVE_COMPARISON :
266f2346221Smrg                               XCB_SYNC_TESTTYPE_NEGATIVE_COMPARISON);
267f2346221Smrg        uint32_t values[] = { test_type, some_values[i] >> 32, some_values[i] };
2681b5d61b8Smrg
269f2346221Smrg        xcb_sync_change_alarm(c, alarm, mask, values);
2701b5d61b8Smrg        queries[i] = xcb_sync_query_alarm_unchecked(c, alarm);
2711b5d61b8Smrg    }
2721b5d61b8Smrg
2731b5d61b8Smrg    for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
2741b5d61b8Smrg        xcb_sync_query_alarm_reply_t *reply =
2751b5d61b8Smrg            xcb_sync_query_alarm_reply(c, queries[i], NULL);
2761b5d61b8Smrg        int64_t value = pack_sync_value(reply->delta);
2771b5d61b8Smrg
2781b5d61b8Smrg        if (value != some_values[i]) {
2791b5d61b8Smrg            fprintf(stderr, "Setting alarm delta to %lld returned %lld\n",
2801b5d61b8Smrg                    (long long)some_values[i],
2811b5d61b8Smrg                    (long long)value);
2821b5d61b8Smrg            exit(1);
2831b5d61b8Smrg        }
2841b5d61b8Smrg        free(reply);
2851b5d61b8Smrg    }
2861b5d61b8Smrg}
2871b5d61b8Smrg
2881b5d61b8Smrgint main(int argc, char **argv)
2891b5d61b8Smrg{
2901b5d61b8Smrg    int screen;
2911b5d61b8Smrg    xcb_connection_t *c = xcb_connect(NULL, &screen);
2921b5d61b8Smrg    const xcb_query_extension_reply_t *ext = xcb_get_extension_data(c, &xcb_sync_id);
2931b5d61b8Smrg
2941b5d61b8Smrg    if (!ext->present) {
2951b5d61b8Smrg        printf("No XSync present\n");
2961b5d61b8Smrg        exit(77);
2971b5d61b8Smrg    }
2981b5d61b8Smrg
2991b5d61b8Smrg    test_create_counter(c);
3001b5d61b8Smrg    test_set_counter(c);
3011b5d61b8Smrg    test_change_counter_basic(c);
3021b5d61b8Smrg    test_change_counter_overflow(c);
3031b5d61b8Smrg    test_change_alarm_value(c);
3041b5d61b8Smrg    test_change_alarm_delta(c);
3051b5d61b8Smrg
3061b5d61b8Smrg    xcb_disconnect(c);
3071b5d61b8Smrg    exit(0);
3081b5d61b8Smrg}
309