xenocara/xserver/test/sync/sync.c
2019-07-27 07:57:06 +00:00

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