mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-28 11:11:47 +02:00
Allocate history buffer segments with mmap
glibc can raise its dynamic mmap threshold above these MB-scale allocations, routing later scrollback segments through brk. Closing windows then leaves the freed segments stranded in allocator bins and grows the long-lived kitty process. Back the existing batched segment layout with anonymous mappings instead. This preserves zero-initialization and pointer layout while ensuring the memory is returned to the OS when a history buffer is cleared or destroyed. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@
|
|||||||
#include "charsets.h"
|
#include "charsets.h"
|
||||||
#include "resize.h"
|
#include "resize.h"
|
||||||
#include <structmember.h>
|
#include <structmember.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
#include "../3rdparty/ringbuf/ringbuf.h"
|
#include "../3rdparty/ringbuf/ringbuf.h"
|
||||||
|
|
||||||
extern PyTypeObject Line_Type;
|
extern PyTypeObject Line_Type;
|
||||||
@@ -22,22 +23,27 @@ add_segment(HistoryBuf *self, index_type num) {
|
|||||||
const size_t cpu_cells_size = self->xnum * SEGMENT_SIZE * sizeof(CPUCell);
|
const size_t cpu_cells_size = self->xnum * SEGMENT_SIZE * sizeof(CPUCell);
|
||||||
const size_t gpu_cells_size = self->xnum * SEGMENT_SIZE * sizeof(GPUCell);
|
const size_t gpu_cells_size = self->xnum * SEGMENT_SIZE * sizeof(GPUCell);
|
||||||
const size_t segment_size = cpu_cells_size + gpu_cells_size + SEGMENT_SIZE * sizeof(LineAttrs);
|
const size_t segment_size = cpu_cells_size + gpu_cells_size + SEGMENT_SIZE * sizeof(LineAttrs);
|
||||||
char *mem = calloc(num, segment_size);
|
if (num > SIZE_MAX / segment_size) fatal("History buffer segment allocation is too large");
|
||||||
if (!mem) fatal("Out of memory allocating new history buffer segment");
|
const size_t mmap_size = num * segment_size;
|
||||||
|
char *mem = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||||
|
if (mem == MAP_FAILED) fatal("Out of memory allocating new history buffer segment");
|
||||||
char *needs_free = mem;
|
char *needs_free = mem;
|
||||||
for (HistoryBufSegment *s = self->segments + self->num_segments; s < self->segments + self->num_segments + num; s++, mem += segment_size) {
|
for (HistoryBufSegment *s = self->segments + self->num_segments; s < self->segments + self->num_segments + num; s++, mem += segment_size) {
|
||||||
s->cpu_cells = (CPUCell*)mem;
|
s->cpu_cells = (CPUCell*)mem;
|
||||||
s->gpu_cells = (GPUCell*)(((uint8_t*)s->cpu_cells) + cpu_cells_size);
|
s->gpu_cells = (GPUCell*)(((uint8_t*)s->cpu_cells) + cpu_cells_size);
|
||||||
s->line_attrs = (LineAttrs*)(((uint8_t*)s->gpu_cells) + gpu_cells_size);
|
s->line_attrs = (LineAttrs*)(((uint8_t*)s->gpu_cells) + gpu_cells_size);
|
||||||
s->mem = NULL;
|
s->mem = NULL;
|
||||||
|
s->mmap_size = 0;
|
||||||
}
|
}
|
||||||
self->segments[self->num_segments].mem = needs_free;
|
self->segments[self->num_segments].mem = needs_free;
|
||||||
|
self->segments[self->num_segments].mmap_size = mmap_size;
|
||||||
self->num_segments += num;
|
self->num_segments += num;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
free_segment(HistoryBufSegment *s) {
|
free_segment(HistoryBufSegment *s) {
|
||||||
free(s->mem); zero_at_ptr(s);
|
if (s->mem && munmap(s->mem, s->mmap_size) != 0) log_error("Failed to unmap history buffer segment: %s", strerror(errno));
|
||||||
|
zero_at_ptr(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static index_type
|
static index_type
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ typedef struct {
|
|||||||
CPUCell *cpu_cells;
|
CPUCell *cpu_cells;
|
||||||
LineAttrs *line_attrs;
|
LineAttrs *line_attrs;
|
||||||
void *mem;
|
void *mem;
|
||||||
|
size_t mmap_size;
|
||||||
} HistoryBufSegment;
|
} HistoryBufSegment;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user