summaryrefslogtreecommitdiff
path: root/src/allocators.zig
blob: 61c55f391b568698d463757bfa1008397dd90c61 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
const std = @import("std");
const builtin = @import("builtin");
const limits = @import("limits.zig");

const Allocator = std.mem.Allocator;
const debug_enabled = builtin.mode == .Debug;

pub const Allocators = struct {
    pardes: Allocator,
    frame: Allocator,
    lsp: Allocator,
    tree_sitter: Allocator,
    image: Allocator,
    pdf: Allocator,
};

/// The static reservations, one per profile tier. See `limits.arena` for why
/// each number is what it is, and why the board's are 4 KiB and zero.
var pardes_fallback: std.heap.StackFallbackAllocator(limits.arena.pardes) = undefined;
var frame_fallback: std.heap.StackFallbackAllocator(limits.arena.frame) = undefined;
var tree_sitter_fallback: std.heap.StackFallbackAllocator(limits.arena.tree_sitter) = undefined;
var image_fallback: std.heap.StackFallbackAllocator(limits.arena.image) = undefined;
var pdf_fallback: std.heap.StackFallbackAllocator(limits.arena.pdf) = undefined;

const Debug = std.heap.DebugAllocator(.{});
var pardes_debug: Debug = .init;
var frame_debug: Debug = .init;
var lsp_debug: Debug = .init;
var tree_sitter_debug: Debug = .init;
var image_debug: Debug = .init;
var pdf_debug: Debug = .init;

/// Returns ordinary allocators backed by stdlib fixed-buffer fallbacks. LSP
/// keeps the caller's allocator because its detached workers are concurrent.
pub fn init(fallback: Allocator) Allocators {
    pardes_fallback.fallback_allocator = fallback;
    pardes_fallback.get_called = if (std.debug.runtime_safety) false else {};
    frame_fallback.fallback_allocator = fallback;
    frame_fallback.get_called = if (std.debug.runtime_safety) false else {};
    tree_sitter_fallback.fallback_allocator = fallback;
    tree_sitter_fallback.get_called = if (std.debug.runtime_safety) false else {};
    image_fallback.fallback_allocator = fallback;
    image_fallback.get_called = if (std.debug.runtime_safety) false else {};
    pdf_fallback.fallback_allocator = fallback;
    pdf_fallback.get_called = if (std.debug.runtime_safety) false else {};

    const raw: Allocators = .{
        .pardes = pardes_fallback.get(),
        .frame = frame_fallback.get(),
        .lsp = fallback,
        .tree_sitter = tree_sitter_fallback.get(),
        .image = image_fallback.get(),
        .pdf = pdf_fallback.get(),
    };
    if (!debug_enabled) return raw;

    pardes_debug = .{ .backing_allocator = raw.pardes };
    frame_debug = .{ .backing_allocator = raw.frame };
    lsp_debug = .{ .backing_allocator = raw.lsp };
    tree_sitter_debug = .{ .backing_allocator = raw.tree_sitter };
    image_debug = .{ .backing_allocator = raw.image };
    pdf_debug = .{ .backing_allocator = raw.pdf };
    return .{
        .pardes = pardes_debug.allocator(),
        .frame = frame_debug.allocator(),
        .lsp = lsp_debug.allocator(),
        .tree_sitter = tree_sitter_debug.allocator(),
        .image = image_debug.allocator(),
        .pdf = pdf_debug.allocator(),
    };
}

pub fn deinit() void {
    if (!debug_enabled) return;
    var leaked = pardes_debug.deinit() == .leak;
    leaked = (frame_debug.deinit() == .leak) or leaked;
    leaked = (lsp_debug.deinit() == .leak) or leaked;
    leaked = (tree_sitter_debug.deinit() == .leak) or leaked;
    leaked = (image_debug.deinit() == .leak) or leaked;
    leaked = (pdf_debug.deinit() == .leak) or leaked;
    if (leaked) @panic("allocator leaks detected");
}

test "fixed allocators are separate, spill, and restart" {
    var allocs = init(std.testing.allocator);
    const core = try allocs.pardes.alloc(u8, 32);
    const frame = try allocs.frame.alloc(u8, 32);
    try std.testing.expect(pardes_fallback.fixed_buffer_allocator.ownsPtr(core.ptr));
    try std.testing.expect(frame_fallback.fixed_buffer_allocator.ownsPtr(frame.ptr));
    try std.testing.expect(core.ptr != frame.ptr);

    const spill = try allocs.pardes.alloc(u8, limits.arena.pardes + 1);
    try std.testing.expect(!pardes_fallback.fixed_buffer_allocator.ownsPtr(spill.ptr));
    allocs.pardes.free(spill);
    allocs.frame.free(frame);
    allocs.pardes.free(core);
    deinit();

    allocs = init(std.testing.allocator);
    defer deinit();
    const restarted = try allocs.pardes.alloc(u8, 32);
    defer allocs.pardes.free(restarted);
    try std.testing.expect(pardes_fallback.fixed_buffer_allocator.ownsPtr(restarted.ptr));
}