wayland/src/wayland-private.h
Manuel Stoeckl d074d52902 connection: Dynamically resize connection buffers
When using fixed size connection buffers, if either the client or the
server is sending requests faster than the other end can cope with, the
connection buffers will fill up, eventually killing the connection.

This can be a problem for example with Xwayland mapping a lot of
windows, faster than the Wayland compositor can cope with, or a
high-rate mouse flooding the Wayland client with pointer events.

To avoid the issue, resize the connection buffers dynamically when they
get full.

Both data and fd buffers are resized on demand.

The default max buffer size is controlled via the wl_display interface
while each client's connection buffer size is adjustable for finer
control.

The purpose is to explicitly have larger connection buffers for specific
clients such as Xwayland, or set a larger buffer size for the client
with pointer focus to deal with a higher input events rate.

v0: Manuel:
   Dynamically resize connection buffers - Both data and fd buffers are
   resized on demand.
v1: Olivier
1. Add support for unbounded buffers on the client side and growable
   (yet limited) connection buffers on the server side.
2. Add the API to set the default maximum size and a limit for a given
   client.
3. Add tests for growable connection buffers and adjustable limits.
v2: Additional fixes by John:
1. Fix the size calculation in ring_buffer_check_space()
2. Fix wl_connection_read() to return gracefully once it has read up to
   the max buffer size, rather than returning an error.
3. If wl_connection_flush() fails with EAGAIN but the transmit
   ring-buffer has space remaining (or can be expanded),
   wl_connection_queue() should store the message rather than
   returning an error.
4. When the receive ring-buffer is at capacity but more data is
   available to be read, wl_connection_read() should attempt to
   expand the ring-buffer in order to read the remaining data.
v3: Thomas Lukaszewicz <tluk@chromium.org>
   Add a test for unbounded buffers
v4: Add a client API as well to force bounded buffers (unbounded
    by default (Olivier)
v5: Simplify ring_buffer_ensure_space() (Sebastian)

Co-authored-by: Olivier Fourdan <ofourdan@redhat.com>
Co-authored-by: John Lindgren <john@jlindgren.net>
Co-authored-by: Sebastian Wick <sebastian@sebastianwick.net>
Signed-off-by: Manuel Stoeckl <code@mstoeckl.com>
Signed-off-by: Olivier Fourdan <ofourdan@redhat.com>
Signed-off-by: John Lindgren <john@jlindgren.net>
Signed-off-by: Sebastian Wick <sebastian@sebastianwick.net>
Closes: https://gitlab.freedesktop.org/wayland/wayland/-/issues/237
2024-04-08 14:05:32 +00:00

261 lines
6.6 KiB
C

/*
* Copyright © 2008-2011 Kristian Høgsberg
* Copyright © 2011 Intel Corporation
* Copyright © 2013 Jason Ekstrand
*
* 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.
*/
#ifndef WAYLAND_PRIVATE_H
#define WAYLAND_PRIVATE_H
#include <stdarg.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#define WL_HIDE_DEPRECATED 1
#include "wayland-util.h"
/* Invalid memory address */
#define WL_ARRAY_POISON_PTR (void *) 4
#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
#define WL_MAP_SERVER_SIDE 0
#define WL_MAP_CLIENT_SIDE 1
#define WL_SERVER_ID_START 0xff000000
#define WL_MAP_MAX_OBJECTS 0x00f00000
#define WL_CLOSURE_MAX_ARGS 20
#define WL_BUFFER_DEFAULT_SIZE_POT 12
#define WL_BUFFER_DEFAULT_MAX_SIZE (1 << WL_BUFFER_DEFAULT_SIZE_POT)
/**
* Argument types used in signatures.
*/
enum wl_arg_type {
WL_ARG_INT = 'i',
WL_ARG_UINT = 'u',
WL_ARG_FIXED = 'f',
WL_ARG_STRING = 's',
WL_ARG_OBJECT = 'o',
WL_ARG_NEW_ID = 'n',
WL_ARG_ARRAY = 'a',
WL_ARG_FD = 'h',
};
struct wl_object {
const struct wl_interface *interface;
const void *implementation;
uint32_t id;
};
int
wl_interface_equal(const struct wl_interface *iface1,
const struct wl_interface *iface2);
/* Flags for wl_map_insert_new and wl_map_insert_at. Flags can be queried with
* wl_map_lookup_flags. The current implementation has room for 1 bit worth of
* flags. If more flags are ever added, the implementation of wl_map will have
* to change to allow for new flags */
enum wl_map_entry_flags {
WL_MAP_ENTRY_LEGACY = (1 << 0), /* Server side only */
WL_MAP_ENTRY_ZOMBIE = (1 << 0) /* Client side only */
};
struct wl_map {
struct wl_array client_entries;
struct wl_array server_entries;
uint32_t side;
uint32_t free_list;
};
typedef enum wl_iterator_result (*wl_iterator_func_t)(void *element,
void *data,
uint32_t flags);
void
wl_map_init(struct wl_map *map, uint32_t side);
void
wl_map_release(struct wl_map *map);
uint32_t
wl_map_insert_new(struct wl_map *map, uint32_t flags, void *data);
int
wl_map_insert_at(struct wl_map *map, uint32_t flags, uint32_t i, void *data);
int
wl_map_reserve_new(struct wl_map *map, uint32_t i);
void
wl_map_remove(struct wl_map *map, uint32_t i);
void *
wl_map_lookup(struct wl_map *map, uint32_t i);
uint32_t
wl_map_lookup_flags(struct wl_map *map, uint32_t i);
void
wl_map_for_each(struct wl_map *map, wl_iterator_func_t func, void *data);
struct wl_connection *
wl_connection_create(int fd, size_t max_buffer_size);
int
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_flush(struct wl_connection *connection);
uint32_t
wl_connection_pending_input(struct wl_connection *connection);
int
wl_connection_read(struct wl_connection *connection);
int
wl_connection_write(struct wl_connection *connection,
const void *data, size_t count);
int
wl_connection_queue(struct wl_connection *connection,
const void *data, size_t count);
int
wl_connection_get_fd(struct wl_connection *connection);
struct wl_closure {
int count;
const struct wl_message *message;
uint32_t opcode;
uint32_t sender_id;
union wl_argument args[WL_CLOSURE_MAX_ARGS];
struct wl_list link;
struct wl_proxy *proxy;
struct wl_array extra[0];
};
struct argument_details {
enum wl_arg_type type;
int nullable;
};
const char *
get_next_argument(const char *signature, struct argument_details *details);
int
arg_count_for_signature(const char *signature);
int
wl_message_count_arrays(const struct wl_message *message);
int
wl_message_get_since(const struct wl_message *message);
void
wl_argument_from_va_list(const char *signature, union wl_argument *args,
int count, va_list ap);
struct wl_closure *
wl_closure_marshal(struct wl_object *sender,
uint32_t opcode, union wl_argument *args,
const struct wl_message *message);
struct wl_closure *
wl_closure_vmarshal(struct wl_object *sender,
uint32_t opcode, va_list ap,
const struct wl_message *message);
struct wl_closure *
wl_connection_demarshal(struct wl_connection *connection,
uint32_t size,
struct wl_map *objects,
const struct wl_message *message);
bool
wl_object_is_zombie(struct wl_map *map, uint32_t id);
int
wl_closure_lookup_objects(struct wl_closure *closure, struct wl_map *objects);
enum wl_closure_invoke_flag {
WL_CLOSURE_INVOKE_CLIENT = (1 << 0),
WL_CLOSURE_INVOKE_SERVER = (1 << 1)
};
void
wl_closure_invoke(struct wl_closure *closure, uint32_t flags,
struct wl_object *target, uint32_t opcode, void *data);
void
wl_closure_dispatch(struct wl_closure *closure, wl_dispatcher_func_t dispatcher,
struct wl_object *target, uint32_t opcode);
int
wl_closure_send(struct wl_closure *closure, struct wl_connection *connection);
int
wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection);
void
wl_closure_print(struct wl_closure *closure,
struct wl_object *target, int send, int discarded,
uint32_t (*n_parse)(union wl_argument *arg),
const char *queue_name);
void
wl_closure_destroy(struct wl_closure *closure);
extern wl_log_func_t wl_log_handler;
void wl_log(const char *fmt, ...);
void wl_abort(const char *fmt, ...);
struct wl_display;
struct wl_array *
wl_display_get_additional_shm_formats(struct wl_display *display);
static inline void *
zalloc(size_t s)
{
return calloc(1, s);
}
void
wl_connection_close_fds_in(struct wl_connection *connection, int max);
void
wl_connection_set_max_buffer_size(struct wl_connection *connection,
size_t max_buffer_size);
#endif