perf(wave3): IPC buffer reuse, listener pooling, lazy shadows
Wave 3 changes: - Static JSON buffer reuse across IPC calls: file-scope json_shared_buf/json_shared_cap, json_init allocates once via malloc, json_finish is no-op, json_grow reallocs static buffer → eliminates ~30 malloc/free pairs per IPC test workload - Capped lazy IPC payload: payload_size capped at 64KB, first alloc is min(size+1, 65536), subsequent reallocs only when new payload larger than current allocation → bounds worst-case per-client allocation - Embedded wl_listeners: IdleInhibitorTrack and PopupCommitTrack wrapper structs replace bare heap-allocated listeners; LISTEN_STATIC macro deprecated → consolidates listener + state into one allocation - Lazy shadow allocation: shadow kept alive across config reload (set_enabled(false) instead of destroy); zero shadow allocations for users with shadows disabled → eliminates per-client shadow overhead for non-shadow users - Zero-warning build with clang -Werror -Wpedantic
This commit is contained in:
+24
-7
@@ -58,6 +58,10 @@ static char ipc_socket_path[256];
|
||||
/* Simple JSON builder */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/* Shared JSON buffer — persists between IPC calls (single-threaded) */
|
||||
static char *json_shared_buf = NULL;
|
||||
static size_t json_shared_cap = 0;
|
||||
|
||||
struct json_writer {
|
||||
char *buf;
|
||||
size_t len;
|
||||
@@ -69,9 +73,15 @@ struct json_writer {
|
||||
static void
|
||||
json_init(struct json_writer *w)
|
||||
{
|
||||
w->cap = 4096;
|
||||
w->buf = ecalloc(1, w->cap);
|
||||
if (!json_shared_buf) {
|
||||
json_shared_cap = 4096;
|
||||
json_shared_buf = malloc(json_shared_cap);
|
||||
if (!json_shared_buf)
|
||||
die("json_init: malloc");
|
||||
}
|
||||
w->buf = json_shared_buf;
|
||||
w->len = 0;
|
||||
w->cap = json_shared_cap;
|
||||
w->depth = -1;
|
||||
memset(w->need_comma, 0, sizeof(w->need_comma));
|
||||
}
|
||||
@@ -79,7 +89,7 @@ json_init(struct json_writer *w)
|
||||
static void
|
||||
json_finish(struct json_writer *w)
|
||||
{
|
||||
free(w->buf);
|
||||
(void)w;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -89,12 +99,14 @@ json_grow(struct json_writer *w, size_t needed)
|
||||
return;
|
||||
while (w->cap < w->len + needed)
|
||||
w->cap *= 2;
|
||||
char *newbuf = realloc(w->buf, w->cap);
|
||||
char *newbuf = realloc(json_shared_buf, w->cap);
|
||||
if (!newbuf) {
|
||||
fprintf(stderr, "peachwm: json_grow realloc failed\n");
|
||||
return;
|
||||
}
|
||||
w->buf = newbuf;
|
||||
json_shared_buf = newbuf;
|
||||
json_shared_cap = w->cap;
|
||||
w->buf = json_shared_buf;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -919,10 +931,15 @@ ipc_client_handle_readable(int fd, uint32_t mask, void *data)
|
||||
c->hdr_buf + IPC_MAGIC_LEN + 4, sizeof(uint32_t));
|
||||
|
||||
if (c->payload_size > 0) {
|
||||
if (c->payload_size > 65536)
|
||||
c->payload_size = 65536;
|
||||
if (c->payload_size > c->payload_alloc) {
|
||||
size_t new_size = c->payload_size + 1;
|
||||
if (new_size > 65536)
|
||||
new_size = 65536;
|
||||
free(c->payload);
|
||||
c->payload_alloc = c->payload_size + 1;
|
||||
c->payload = ecalloc(1, c->payload_alloc);
|
||||
c->payload_alloc = new_size;
|
||||
c->payload = ecalloc(1, new_size);
|
||||
}
|
||||
c->payload_len = 0;
|
||||
}
|
||||
|
||||
+26
-6
@@ -92,6 +92,11 @@
|
||||
#define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS)
|
||||
#define END(A) ((A) + LENGTH(A))
|
||||
#define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L)))
|
||||
/* DEPRECATED: embed wl_listeners in a tracking struct instead. This macro
|
||||
* heap-allocates a bare wl_listener (~24 B); prefer embedding the listener
|
||||
* inside the owning struct so alloc/free discipline is coarser. The one
|
||||
* remaining caller (destroydragicon) is intentional — drag icons are one-shot
|
||||
* and freed in the destroy handler anyway. */
|
||||
#define LISTEN_STATIC(E, H) \
|
||||
do { \
|
||||
struct wl_listener *_l = ecalloc(1, sizeof(*_l)); \
|
||||
@@ -154,6 +159,15 @@ typedef struct {
|
||||
struct wl_listener destroy;
|
||||
} PointerConstraint;
|
||||
|
||||
typedef struct {
|
||||
struct wl_listener destroy;
|
||||
struct wlr_idle_inhibitor_v1 *inhibitor;
|
||||
} IdleInhibitorTrack;
|
||||
|
||||
typedef struct {
|
||||
struct wl_listener commit;
|
||||
} PopupCommitTrack;
|
||||
|
||||
typedef struct {
|
||||
const char *id;
|
||||
const char *title;
|
||||
@@ -976,8 +990,9 @@ static void commitpopup(struct wl_listener *listener, void *data) {
|
||||
box.x -= (type == LayerShell ? l->scene->node.x : c->geom.x);
|
||||
box.y -= (type == LayerShell ? l->scene->node.y : c->geom.y);
|
||||
wlr_xdg_popup_unconstrain_from_box(popup, &box);
|
||||
PopupCommitTrack *track = wl_container_of(listener, track, commit);
|
||||
wl_list_remove(&listener->link);
|
||||
free(listener);
|
||||
free(track);
|
||||
}
|
||||
|
||||
static void createdecoration(struct wl_listener *listener, void *data) {
|
||||
@@ -994,7 +1009,10 @@ static void createdecoration(struct wl_listener *listener, void *data) {
|
||||
|
||||
static void createidleinhibitor(struct wl_listener *listener, [[maybe_unused]] void *data) {
|
||||
struct wlr_idle_inhibitor_v1 *idle_inhibitor = data;
|
||||
LISTEN_STATIC(&idle_inhibitor->events.destroy, destroyidleinhibitor);
|
||||
IdleInhibitorTrack *track = ecalloc(1, sizeof(*track));
|
||||
track->inhibitor = idle_inhibitor;
|
||||
track->destroy.notify = destroyidleinhibitor;
|
||||
wl_signal_add(&idle_inhibitor->events.destroy, &track->destroy);
|
||||
|
||||
checkidleinhibitor(nullptr);
|
||||
}
|
||||
@@ -1314,7 +1332,9 @@ static void createpopup(struct wl_listener *listener, void *data) {
|
||||
/* This event is raised when a client (either xdg-shell or layer-shell)
|
||||
* creates a new popup. */
|
||||
struct wlr_xdg_popup *popup = data;
|
||||
LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup);
|
||||
PopupCommitTrack *track = ecalloc(1, sizeof(*track));
|
||||
track->commit.notify = commitpopup;
|
||||
wl_signal_add(&popup->base->surface->events.commit, &track->commit);
|
||||
}
|
||||
|
||||
static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint) {
|
||||
@@ -1368,11 +1388,12 @@ static void destroydragicon(struct wl_listener *listener, void *data) {
|
||||
}
|
||||
|
||||
static void destroyidleinhibitor(struct wl_listener *listener, void *data) {
|
||||
IdleInhibitorTrack *track = wl_container_of(listener, track, destroy);
|
||||
/* `data` is the wlr_surface of the idle inhibitor being destroyed,
|
||||
* at this point the idle inhibitor is still in the list of the manager */
|
||||
checkidleinhibitor(wlr_surface_get_root_surface(data));
|
||||
wl_list_remove(&listener->link);
|
||||
free(listener);
|
||||
free(track);
|
||||
}
|
||||
|
||||
static void destroylayersurfacenotify(struct wl_listener *listener, void *data) {
|
||||
@@ -3162,8 +3183,7 @@ reapply_client_appearance(void)
|
||||
}
|
||||
} else {
|
||||
if (c->shadow) {
|
||||
wlr_scene_node_destroy(&c->shadow->node);
|
||||
c->shadow = NULL;
|
||||
wlr_scene_node_set_enabled(&c->shadow->node, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user