From 680f1c702515c3ddcd4d819ee840a92787d657ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Wed, 8 Oct 2008 12:48:46 -0400 Subject: [PATCH] Split out connection io buffer logic. --- Makefile | 2 +- connection.c | 193 +++++++++++++++++++++++++++++++++++ connection.h | 21 ++++ wayland.c | 283 ++++++++++++++++----------------------------------- 4 files changed, 303 insertions(+), 196 deletions(-) create mode 100644 connection.c create mode 100644 connection.h diff --git a/Makefile b/Makefile index 44a0b046..d8fd4ed3 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ LDLIBS += $(shell pkg-config --libs libffi libdrm) all : wayland client -wayland_objs = wayland.o event-loop.o hash.o compositor.o +wayland_objs = wayland.o event-loop.o connection.o hash.o compositor.o wayland : $(wayland_objs) gcc -o $@ $(wayland_objs) $(LDLIBS) diff --git a/connection.c b/connection.c new file mode 100644 index 00000000..9b60af03 --- /dev/null +++ b/connection.c @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#include +#include + +#include "connection.h" + +#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) + +struct wl_buffer { + char data[4096]; + int head, tail; +}; + +struct wl_connection { + struct wl_buffer in, out; + int fd; + void *data; + wl_connection_update_func_t update; +}; + +struct wl_connection * +wl_connection_create(int fd, + wl_connection_update_func_t update, + void *data) +{ + struct wl_connection *connection; + + connection = malloc(sizeof *connection); + memset(connection, 0, sizeof *connection); + connection->fd = fd; + connection->update = update; + connection->data = data; + + connection->update(connection, + WL_CONNECTION_READABLE, + connection->data); + + return connection; +} + +void +wl_connection_destroy(struct wl_connection *connection) +{ + free(connection); +} + +void +wl_connection_copy(struct wl_connection *connection, void *data, size_t size) +{ + struct wl_buffer *b; + int tail, rest; + + b = &connection->in; + tail = b->tail; + if (tail + size <= ARRAY_LENGTH(b->data)) { + memcpy(data, b->data + tail, size); + } else { + rest = ARRAY_LENGTH(b->data) - tail; + memcpy(data, b->data + tail, rest); + memcpy(data + rest, b->data, size - rest); + } +} + +void +wl_connection_consume(struct wl_connection *connection, size_t size) +{ + struct wl_buffer *b; + int tail, rest; + + b = &connection->in; + tail = b->tail; + if (tail + size <= ARRAY_LENGTH(b->data)) { + b->tail += size; + } else { + rest = ARRAY_LENGTH(b->data) - tail; + b->tail = size - rest; + } +} + +int wl_connection_data(struct wl_connection *connection, uint32_t mask) +{ + struct wl_buffer *b; + struct iovec iov[2]; + int len, head, tail, count, size, available; + + if (mask & WL_CONNECTION_READABLE) { + b = &connection->in; + head = connection->in.head; + if (head < b->tail) { + iov[0].iov_base = b->data + head; + iov[0].iov_len = b->tail - head; + count = 1; + } else { + size = ARRAY_LENGTH(b->data) - head; + iov[0].iov_base = b->data + head; + iov[0].iov_len = size; + iov[1].iov_base = b->data; + iov[1].iov_len = b->tail; + count = 2; + } + len = readv(connection->fd, iov, count); + if (len < 0) { + fprintf(stderr, + "read error from connection %p: %m (%d)\n", + connection, errno); + return -1; + } else if (len == 0) { + /* FIXME: Handle this better? */ + return -1; + } else if (head + len <= ARRAY_LENGTH(b->data)) { + b->head += len; + } else { + b->head = head + len - ARRAY_LENGTH(b->data); + } + + /* We know we have data in the buffer at this point, + * so if head equals tail, it means the buffer is + * full. */ + + available = b->head - b->tail; + if (available == 0) + available = sizeof b->data; + else if (available < 0) + available += ARRAY_LENGTH(b->data); + } else { + available = 0; + } + + if (mask & WL_CONNECTION_WRITABLE) { + b = &connection->out; + tail = b->tail; + if (tail < b->head) { + iov[0].iov_base = b->data + tail; + iov[0].iov_len = b->head - tail; + count = 1; + } else { + size = ARRAY_LENGTH(b->data) - tail; + iov[0].iov_base = b->data + tail; + iov[0].iov_len = size; + iov[1].iov_base = b->data; + iov[1].iov_len = b->head; + count = 2; + } + len = writev(connection->fd, iov, count); + if (len < 0) { + fprintf(stderr, "write error for connection %p: %m\n", connection); + return -1; + } else if (tail + len <= ARRAY_LENGTH(b->data)) { + b->tail += len; + } else { + b->tail = tail + len - ARRAY_LENGTH(b->data); + } + + /* We just took data out of the buffer, so at this + * point if head equals tail, the buffer is empty. */ + + if (b->tail == b->head) + connection->update(connection, + WL_CONNECTION_READABLE, + connection->data); + } + + return available; +} + +void +wl_connection_write(struct wl_connection *connection, const void *data, size_t count) +{ + struct wl_buffer *b; + size_t size; + int head; + + b = &connection->out; + head = b->head; + if (head + count <= ARRAY_LENGTH(b->data)) { + memcpy(b->data + head, data, count); + b->head += count; + } else { + size = ARRAY_LENGTH(b->data) - head; + memcpy(b->data + head, data, size); + memcpy(b->data, data + size, count - size); + b->head = count - size; + } + + if (b->tail == head) + connection->update(connection, + WL_CONNECTION_READABLE | + WL_CONNECTION_WRITABLE, + connection->data); +} diff --git a/connection.h b/connection.h new file mode 100644 index 00000000..0e75e2c1 --- /dev/null +++ b/connection.h @@ -0,0 +1,21 @@ +#ifndef _CONNECTION_H_ +#define _CONNECTION_H_ + +struct wl_connection; + +#define WL_CONNECTION_READABLE 0x01 +#define WL_CONNECTION_WRITABLE 0x02 + +typedef int (*wl_connection_update_func_t)(struct wl_connection *connection, + uint32_t mask, void *data); + +struct wl_connection *wl_connection_create(int fd, + wl_connection_update_func_t update, + void *data); +void wl_connection_destroy(struct wl_connection *connection); +void wl_connection_copy(struct wl_connection *connection, void *data, size_t size); +void wl_connection_consume(struct wl_connection *connection, size_t size); +int wl_connection_data(struct wl_connection *connection, uint32_t mask); +void wl_connection_write(struct wl_connection *connection, const void *data, size_t count); + +#endif diff --git a/wayland.c b/wayland.c index 84ba691b..6bad7183 100644 --- a/wayland.c +++ b/wayland.c @@ -7,22 +7,18 @@ #include #include #include -#include #include + #include "wayland.h" +#include "connection.h" #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) struct wl_region { }; -struct wl_buffer { - char data[4096]; - int head, tail; -}; - struct wl_client { - struct wl_buffer in, out; + struct wl_connection *connection; struct wl_event_source *source; struct wl_display *display; }; @@ -156,83 +152,8 @@ wl_surface_get_data(struct wl_surface *surface) return surface->compositor_data; } -static void -wl_client_data(int fd, uint32_t mask, void *data); -static void -wl_client_write(struct wl_client *client, const void *data, size_t count); - -static void -advertise_object(struct wl_client *client, struct wl_object *object) -{ - const struct wl_interface *interface; - static const char pad[4]; - uint32_t length, p[2]; - - interface = object->interface; - length = strlen(interface->name); - p[0] = object->id; - p[1] = length; - wl_client_write(client, p, sizeof p); - wl_client_write(client, interface->name, length); - wl_client_write(client, pad, -length & 3); -} - -struct wl_client * -wl_client_create(struct wl_display *display, int fd) -{ - struct wl_client *client; - - client = malloc(sizeof *client); - if (client == NULL) - return NULL; - - memset(client, 0, sizeof *client); - client->display = display; - client->source = wl_event_loop_add_fd(display->loop, fd, - WL_EVENT_READABLE, - wl_client_data, client); - - advertise_object(client, &display->base); - - return client; -} - void -wl_client_destroy(struct wl_client *client) -{ - printf("disconnect from client %p\n", client); - wl_event_loop_remove_source(client->display->loop, client->source); - free(client); -} - -static void -wl_client_copy(struct wl_client *client, void *data, size_t size) -{ - int tail, rest; - - tail = client->in.tail; - if (tail + size <= ARRAY_LENGTH(client->in.data)) { - memcpy(data, client->in.data + tail, size); - } else { - rest = ARRAY_LENGTH(client->in.data) - tail; - memcpy(data, client->in.data + tail, rest); - memcpy(data + rest, client->in.data, size - rest); - } -} - -static void -wl_client_consume(struct wl_client *client, size_t size) -{ - int tail, rest; - - tail = client->in.tail; - if (tail + size <= ARRAY_LENGTH(client->in.data)) { - client->in.tail += size; - } else { - rest = ARRAY_LENGTH(client->in.data) - tail; - client->in.tail = size - rest; - } -} +wl_client_destroy(struct wl_client *client); static void wl_client_demarshal(struct wl_client *client, struct wl_object *target, @@ -270,7 +191,7 @@ wl_client_demarshal(struct wl_client *client, struct wl_object *target, values[1].object = target; args[1] = &values[1]; - wl_client_copy(client, data, size); + wl_connection_copy(client->connection, data, size); p = &data[2]; for (i = 0; i < method->argument_count; i++) { switch (method->arguments[i].type) { @@ -314,31 +235,6 @@ wl_client_demarshal(struct wl_client *client, struct wl_object *target, ffi_call(&cif, FFI_FN(method->func), &result, args); } -static void -wl_client_write(struct wl_client *client, const void *data, size_t count) -{ - size_t size; - int head; - - head = client->out.head; - if (head + count <= ARRAY_LENGTH(client->out.data)) { - memcpy(client->out.data + head, data, count); - client->out.head += count; - } else { - size = ARRAY_LENGTH(client->out.data) - head; - memcpy(client->out.data + head, data, size); - memcpy(client->out.data, data + size, count - size); - client->out.head = count - size; - } - - if (client->out.tail == head) - wl_event_loop_update_source(client->display->loop, - client->source, - WL_EVENT_READABLE | - WL_EVENT_WRITEABLE); -} - - static void wl_client_event(struct wl_client *client, struct wl_object *object, uint32_t event) { @@ -349,31 +245,39 @@ wl_client_event(struct wl_client *client, struct wl_object *object, uint32_t eve p[0] = object->id; p[1] = event | (8 << 16); - wl_client_write(client, p, sizeof p); + wl_connection_write(client->connection, p, sizeof p); } #define WL_DISPLAY_INVALID_OBJECT 0 #define WL_DISPLAY_INVALID_METHOD 1 static void -wl_client_process_input(struct wl_client *client) +wl_client_connection_data(int fd, uint32_t mask, void *data) { + struct wl_client *client = data; + struct wl_connection *connection = client->connection; const struct wl_method *method; struct wl_object *object; - uint32_t p[2], opcode, size, available; - - /* We know we have data in the buffer at this point, so if - * head equals tail, it means the buffer is full. */ - - available = client->in.head - client->in.tail; - if (available <= 0) - available = sizeof client->in.data; + uint32_t p[2], opcode, size; + uint32_t cmask = 0; + int len; + + if (mask & WL_EVENT_READABLE) + cmask |= WL_CONNECTION_READABLE; + if (mask & WL_EVENT_WRITEABLE) + cmask |= WL_CONNECTION_WRITABLE; + + len = wl_connection_data(connection, cmask); + if (len < 0) { + wl_client_destroy(client); + return; + } - while (available > sizeof p) { - wl_client_copy(client, p, sizeof p); + while (len > sizeof p) { + wl_connection_copy(connection, p, sizeof p); opcode = p[1] & 0xffff; size = p[1] >> 16; - if (available < size) + if (len < size) break; object = wl_hash_lookup(&client->display->objects, @@ -381,99 +285,88 @@ wl_client_process_input(struct wl_client *client) if (object == NULL) { wl_client_event(client, &client->display->base, WL_DISPLAY_INVALID_OBJECT); - wl_client_consume(client, size); - available -= size; + wl_connection_consume(connection, size); + len -= size; continue; } if (opcode >= object->interface->method_count) { wl_client_event(client, &client->display->base, WL_DISPLAY_INVALID_METHOD); - wl_client_consume(client, size); - available -= size; + wl_connection_consume(connection, size); + len -= size; continue; } method = &object->interface->methods[opcode]; wl_client_demarshal(client, object, method, size); - wl_client_consume(client, size); - available -= size; + wl_connection_consume(connection, size); + len -= size; } } -static void -wl_client_data(int fd, uint32_t mask, void *data) +static int +wl_client_connection_update(struct wl_connection *connection, + uint32_t mask, void *data) { struct wl_client *client = data; - struct iovec iov[2]; - int len, head, tail, count, size; - - if (mask & WL_EVENT_READABLE) { - head = client->in.head; - if (head < client->in.tail) { - iov[0].iov_base = client->in.data + head; - iov[0].iov_len = client->in.tail - head; - count = 1; - } else { - size = ARRAY_LENGTH(client->out.data) - head; - iov[0].iov_base = client->in.data + head; - iov[0].iov_len = size; - iov[1].iov_base = client->in.data; - iov[1].iov_len = client->in.tail; - count = 2; - } - len = readv(fd, iov, count); - if (len < 0) { - fprintf(stderr, - "read error from client %p: %m (%d)\n", - client, errno); - wl_client_destroy(client); - } else if (len == 0) { - wl_client_destroy(client); - } else if (head + len <= ARRAY_LENGTH(client->in.data)) { - client->in.head += len; - } else { - client->in.head = - head + len - ARRAY_LENGTH(client->in.data); - } + uint32_t emask = 0; - if (client->in.head != client->in.tail) - wl_client_process_input(client); - } + if (mask & WL_CONNECTION_READABLE) + emask |= WL_EVENT_READABLE; + if (mask & WL_CONNECTION_WRITABLE) + emask |= WL_EVENT_WRITEABLE; - if (mask & WL_EVENT_WRITEABLE) { - tail = client->out.tail; - if (tail < client->out.head) { - iov[0].iov_base = client->out.data + tail; - iov[0].iov_len = client->out.head - tail; - count = 1; - } else { - size = ARRAY_LENGTH(client->out.data) - tail; - iov[0].iov_base = client->out.data + tail; - iov[0].iov_len = size; - iov[1].iov_base = client->out.data; - iov[1].iov_len = client->out.head; - count = 2; - } - len = writev(fd, iov, count); - if (len < 0) { - fprintf(stderr, "write error for client %p: %m\n", client); - wl_client_destroy(client); - } else if (tail + len <= ARRAY_LENGTH(client->out.data)) { - client->out.tail += len; - } else { - client->out.tail = - tail + len - ARRAY_LENGTH(client->out.data); - } + return wl_event_loop_update_source(client->display->loop, + client->source, mask); +} - /* We just took data out of the buffer, so at this - * point if head equals tail, the buffer is empty. */ +static void +advertise_object(struct wl_client *client, struct wl_object *object) +{ + const struct wl_interface *interface; + static const char pad[4]; + uint32_t length, p[2]; - if (client->out.tail == client->out.head) - wl_event_loop_update_source(client->display->loop, - client->source, - WL_EVENT_READABLE); - } + interface = object->interface; + length = strlen(interface->name); + p[0] = object->id; + p[1] = length; + wl_connection_write(client->connection, p, sizeof p); + wl_connection_write(client->connection, interface->name, length); + wl_connection_write(client->connection, pad, -length & 3); +} + +struct wl_client * +wl_client_create(struct wl_display *display, int fd) +{ + struct wl_client *client; + + client = malloc(sizeof *client); + if (client == NULL) + return NULL; + + memset(client, 0, sizeof *client); + client->display = display; + client->source = wl_event_loop_add_fd(display->loop, fd, + WL_EVENT_READABLE, + wl_client_connection_data, client); + client->connection = wl_connection_create(fd, + wl_client_connection_update, + client); + + advertise_object(client, &display->base); + + return client; +} + +void +wl_client_destroy(struct wl_client *client) +{ + printf("disconnect from client %p\n", client); + wl_event_loop_remove_source(client->display->loop, client->source); + wl_connection_destroy(client->connection); + free(client); } static int