gemstone/src/mem/cache.c

272 lines
8.1 KiB
C

//
// Created by servostar on 6/5/24.
//
#include <mem/cache.h>
#include <sys/log.h>
#include <glib.h>
#include <string.h>
#include <assert.h>
static GHashTable* namespaces = NULL;
typedef struct MemoryNamespaceStatistic_t {
size_t bytes_allocated;
size_t allocation_count;
size_t reallocation_count;
size_t manual_free_count;
size_t faulty_reallocations;
size_t faulty_allocations;
size_t purged_free_count;
} MemoryNamespaceStatistic;
typedef struct MemoryNamespace_t {
MemoryNamespaceStatistic statistic;
GArray* blocks;
} MemoryNamespace;
typedef MemoryNamespace* MemoryNamespaceRef;
static void namespace_statistics_print(MemoryNamespaceStatistic* memoryNamespaceStatistic, char* name) {
printf("Memory namespace statistics: `%s`\n", name);
printf("------------------------------\n");
printf(" allocated bytes: %ld\n", memoryNamespaceStatistic->bytes_allocated);
printf(" allocations: %ld\n", memoryNamespaceStatistic->allocation_count);
printf(" reallocations: %ld\n", memoryNamespaceStatistic->reallocation_count);
printf(" frees: %ld\n", memoryNamespaceStatistic->manual_free_count);
printf(" faulty allocations: %ld\n", memoryNamespaceStatistic->faulty_allocations);
printf(" faulty reallocations: %ld\n", memoryNamespaceStatistic->faulty_reallocations);
printf(" purged allocations: %ld\n", memoryNamespaceStatistic->purged_free_count);
printf("\n");
}
static void* namespace_malloc(MemoryNamespaceRef memoryNamespace, size_t size) {
assert(memoryNamespace != NULL);
assert(size != 0);
void* block = malloc(size);
if (block == NULL) {
memoryNamespace->statistic.faulty_allocations ++;
} else {
g_array_append_val(memoryNamespace->blocks, block);
memoryNamespace->statistic.allocation_count ++;
memoryNamespace->statistic.bytes_allocated += size;
}
return block;
}
static gboolean namespace_free(MemoryNamespaceRef memoryNamespace, void* block) {
for (guint i = 0; i < memoryNamespace->blocks->len; i++) {
void* current_block = g_array_index(memoryNamespace->blocks, void*, i);
if (current_block == block) {
assert(block != NULL);
free(block);
g_array_remove_index(memoryNamespace->blocks, i);
memoryNamespace->statistic.manual_free_count++;
return TRUE;
}
}
return FALSE;
}
static void* namespace_realloc(MemoryNamespaceRef memoryNamespace, void* block, size_t size) {
void* reallocated_block = NULL;
for (guint i = 0; i < memoryNamespace->blocks->len; i++) {
void* current_block = g_array_index(memoryNamespace->blocks, void*, i);
if (current_block == block) {
reallocated_block = realloc(block, size);
if (reallocated_block != NULL) {
g_array_index(memoryNamespace->blocks, void*, i) = reallocated_block;
memoryNamespace->statistic.bytes_allocated += size;
memoryNamespace->statistic.reallocation_count ++;
} else {
memoryNamespace->statistic.faulty_reallocations++;
}
break;
}
}
return reallocated_block;
}
static void namespace_delete(MemoryNamespaceRef memoryNamespace) {
g_array_free(memoryNamespace->blocks, TRUE);
free(memoryNamespace);
}
static void namespace_purge(MemoryNamespaceRef memoryNamespace) {
for (guint i = 0; i < memoryNamespace->blocks->len; i++) {
void* current_block = g_array_index(memoryNamespace->blocks, void*, i);
free(current_block);
memoryNamespace->statistic.purged_free_count ++;
}
g_array_remove_range(memoryNamespace->blocks, 0, memoryNamespace->blocks->len);
}
static MemoryNamespaceRef namespace_new() {
MemoryNamespaceRef memoryNamespace = malloc(sizeof(MemoryNamespace));
memoryNamespace->blocks = g_array_new(FALSE, FALSE, sizeof(void*));
memoryNamespace->statistic.bytes_allocated = 0;
memoryNamespace->statistic.allocation_count = 0;
memoryNamespace->statistic.manual_free_count = 0;
memoryNamespace->statistic.faulty_reallocations = 0;
memoryNamespace->statistic.faulty_allocations = 0;
memoryNamespace->statistic.purged_free_count = 0;
memoryNamespace->statistic.reallocation_count = 0;
return memoryNamespace;
}
static void cleanup() {
if (namespaces == NULL) {
printf("==> Memory cache was unused <==\n");
return;
}
GHashTableIter iter;
char* name = NULL;
MemoryNamespaceRef memoryNamespace = NULL;
g_hash_table_iter_init(&iter, namespaces);
while (g_hash_table_iter_next(&iter, (gpointer) &name, (gpointer) &memoryNamespace)) {
assert(name != NULL);
assert(memoryNamespace != NULL);
namespace_purge(memoryNamespace);
namespace_delete(memoryNamespace);
}
}
void mem_init() {
atexit(cleanup);
}
static MemoryNamespaceRef check_namespace(MemoryNamespaceName name) {
if (namespaces == NULL) {
namespaces = g_hash_table_new(g_str_hash, g_str_equal);
}
if (g_hash_table_contains(namespaces, name)) {
return g_hash_table_lookup(namespaces, name);
} else {
MemoryNamespaceRef namespace = namespace_new();
g_hash_table_insert(namespaces, name, namespace);
return namespace;
}
}
void *mem_alloc(MemoryNamespaceName name, size_t size) {
MemoryNamespaceRef cache = check_namespace(name);
if (cache == NULL) {
PANIC("memory namespace not created");
}
return namespace_malloc(cache, size);
}
void *mem_realloc(MemoryNamespaceName name, void *ptr, size_t size) {
MemoryNamespaceRef cache = check_namespace(name);
if (cache == NULL) {
PANIC("memory namespace not created");
}
return namespace_realloc(cache, ptr, size);
}
void mem_free_from(MemoryNamespaceName name, void *memory) {
MemoryNamespaceRef cache = check_namespace(name);
namespace_free(cache, memory);
}
void mem_free(void* memory) {
GHashTableIter iter;
char* name;
MemoryNamespaceRef memoryNamespace;
g_hash_table_iter_init(&iter, namespaces);
while (g_hash_table_iter_next(&iter, (gpointer) &name, (gpointer) &memoryNamespace)) {
if (namespace_free(memoryNamespace, memory)) {
break;
}
}
}
void mem_purge_namespace(MemoryNamespaceName name) {
if (g_hash_table_contains(namespaces, name)) {
MemoryNamespaceRef cache = g_hash_table_lookup(namespaces, name);
namespace_purge(cache);
} else {
PANIC("purging invalid namespace: %s", name);
}
}
char* mem_strdup(MemoryNamespaceName name, char* string) {
return mem_clone(name, string, strlen(string) + 1);
}
void* mem_clone(MemoryNamespaceName name, void* data, size_t size) {
void *clone = mem_alloc(name, size);
memcpy(clone, data, size);
return clone;
}
void print_memory_statistics() {
GHashTableIter iter;
char* name;
MemoryNamespaceRef memoryNamespace;
MemoryNamespaceStatistic total;
total.bytes_allocated = 0;
total.faulty_reallocations = 0;
total.faulty_allocations = 0;
total.manual_free_count = 0;
total.allocation_count = 0;
total.purged_free_count = 0;
total.reallocation_count = 0;
g_hash_table_iter_init(&iter, namespaces);
while (g_hash_table_iter_next(&iter, (gpointer) &name, (gpointer) &memoryNamespace)) {
namespace_statistics_print(&memoryNamespace->statistic, name);
total.bytes_allocated += memoryNamespace->statistic.bytes_allocated;
total.faulty_reallocations += memoryNamespace->statistic.faulty_reallocations;
total.faulty_allocations += memoryNamespace->statistic.faulty_allocations;
total.manual_free_count += memoryNamespace->statistic.manual_free_count;
total.allocation_count += memoryNamespace->statistic.allocation_count;
total.purged_free_count += memoryNamespace->statistic.purged_free_count;
total.reallocation_count += memoryNamespace->statistic.reallocation_count;
}
namespace_statistics_print(&total, "summary");
printf("Note: untracked are memory allocations from external libraries.\n");
}