aboutsummaryrefslogtreecommitdiff
path: root/source/memory.c
blob: d7618a787438ef1bccd2fc6b5bbf932cb0045496 (plain)
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
105
106
107
108
109
110
111
112

#include "kpl.h"

void kpl_memory_allocator_init(kpl_memory_allocator *memory_allocator) {
    for (int32_t slab_pow2 = KPL_MEMORY_ALLOCATOR_MIN_SIZE_POW2;
        slab_pow2 <= KPL_MEMORY_ALLOCATOR_MAX_SIZE_POW2; slab_pow2++) {
        kpl_memory_allocator_slab_group *slab_group = &memory_allocator->slabs[slab_pow2];
        slab_group->slab_size_pow2 = slab_pow2;
        slab_group->slab_head = slab_group->slab_tail = NULL;
        kpl_object_queue_init(&slab_group->object_pool);
    }
}

static void kpl_memory_allocator_slab_free(kpl_memory_allocator_slab_group *slab_group) {
    kpl_memory_allocator_slab *slab_head = slab_group->slab_head;
    while (slab_head) {
        kpl_memory_allocator_slab *slab_tmp = slab_head;
        slab_head = slab_head->slab_next;
        munmap(slab_tmp->buffer, slab_tmp->buffer_size);
        free(slab_tmp);
    }
}

void kpl_memory_allocator_free(kpl_memory_allocator *memory_allocator) {
    for (int32_t slab_pow2 = KPL_MEMORY_ALLOCATOR_MIN_SIZE_POW2;
        slab_pow2 < KPL_MEMORY_ALLOCATOR_SLAB_COUNT; slab_pow2++) {
        kpl_memory_allocator_slab_group *slab_group = &memory_allocator->slabs[slab_pow2];
#ifdef KPL_VALGRIND
        kpl_object *object = NULL;
        while ((object = kpl_object_queue_dequeue(&slab_group->object_pool)))
            VALGRIND_FREELIKE_BLOCK(object, 0);
#endif
        kpl_memory_allocator_slab_free(slab_group);
    }
}

static int32_t kpl_memory_allocator_get_size_pow2(int32_t size) {
    return size > 1 ? 32 - __builtin_clz(size - 1) : 0;
}

static const int32_t pages_per_pow2[] = {
    [5]     = 5,
    [6]     = 10,
    [7]     = 15,
    [8]     = 20,
    [9]     = 25,
    [10]    = 30,
    [11]    = 35,
    [12]    = 40,
    [13]    = 2,
    [14]    = 4,
    [15]    = 8,
    [16]    = 16,
    [17]    = 32,
    [18]    = 64,
    [19]    = 128,
    [20]    = 256,
    [21]    = 512,
    [22]    = 1024,
};

static kpl_memory_allocator_slab *kpl_memory_allocator_slab_init(int32_t size_pow2) {
    kpl_memory_allocator_slab *slab = calloc(1, sizeof(kpl_memory_allocator_slab));
    slab->buffer_size = getpagesize() * pages_per_pow2[size_pow2];
    slab->buffer = mmap(NULL, slab->buffer_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
    return slab;
}

static void *kpl_memory_allocator_slab_bump(kpl_memory_allocator_slab *slab, int32_t size) {
    void *object = slab->buffer + slab->buffer_used;
    slab->buffer_used += size;
    return object;
}

static kpl_object *kpl_memory_allocator_slab_allocate(kpl_memory_allocator_slab_group *slab_group,
    int32_t thread_id) {
    kpl_memory_allocator_slab *slab = slab_group->slab_tail;
    int32_t size = 1 << slab_group->slab_size_pow2;
    if (!slab || slab->buffer_used + size > slab->buffer_size) {
        slab = kpl_memory_allocator_slab_init(slab_group->slab_size_pow2);
        if (!slab_group->slab_head)
            slab_group->slab_head = slab_group->slab_tail = slab;
        else
            slab_group->slab_tail = slab_group->slab_tail->slab_next = slab;
    }
    kpl_object *object = kpl_memory_allocator_slab_bump(slab, size);
#ifdef KPL_VALGRIND
    VALGRIND_MALLOCLIKE_BLOCK(object, size, 0, 0);
#endif
    __atomic_store_n(&object->queue_next, NULL, __ATOMIC_RELAXED);
    object->object_size = size;
    object->thread_id = thread_id;
    return object;
}

void *kpl_memory_allocator_allocate(kpl_memory_allocator *memory_allocator, int32_t size, int32_t thread_id) {
    if (size < (1 << KPL_MEMORY_ALLOCATOR_MIN_ROUND_SIZE_POW2))
        return NULL;
    int32_t size_pow2 = kpl_memory_allocator_get_size_pow2(size);
    if (size_pow2 < KPL_MEMORY_ALLOCATOR_MIN_SIZE_POW2)
        size_pow2 = KPL_MEMORY_ALLOCATOR_MIN_SIZE_POW2;
    kpl_memory_allocator_slab_group *slab_group = &memory_allocator->slabs[size_pow2];
    kpl_object *object = kpl_object_queue_dequeue(&slab_group->object_pool);
    if (!object)
        object = kpl_memory_allocator_slab_allocate(slab_group, thread_id);
    return object;
}

void kpl_memory_allocator_return(kpl_memory_allocator *memory_allocator, void *object) {
    int32_t size_pow2 = kpl_memory_allocator_get_size_pow2(((kpl_object*) object)->object_size);
    kpl_object_queue_enqueue(&memory_allocator->slabs[size_pow2].object_pool, object);
}