mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-28 19:21:38 +02:00
Migrate disk cache to verstable
This commit is contained in:
@@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
#include "disk-cache.h"
|
#include "disk-cache.h"
|
||||||
#include "safe-wrappers.h"
|
#include "safe-wrappers.h"
|
||||||
#include "kitty-uthash.h"
|
|
||||||
#include "simd-string.h"
|
#include "simd-string.h"
|
||||||
#include "loop-utils.h"
|
#include "loop-utils.h"
|
||||||
#include "fast-file-copy.h"
|
#include "fast-file-copy.h"
|
||||||
@@ -21,17 +20,34 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
typedef struct CacheKey {
|
||||||
|
void *hash_key;
|
||||||
|
unsigned short hash_keylen;
|
||||||
|
} CacheKey;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void *hash_key;
|
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
size_t data_sz;
|
size_t data_sz;
|
||||||
unsigned short hash_keylen;
|
|
||||||
bool written_to_disk;
|
bool written_to_disk;
|
||||||
off_t pos_in_cache_file;
|
off_t pos_in_cache_file;
|
||||||
uint8_t encryption_key[64];
|
uint8_t encryption_key[64];
|
||||||
UT_hash_handle hh;
|
} CacheValue;
|
||||||
} CacheEntry;
|
|
||||||
|
|
||||||
|
#define NAME cache_map
|
||||||
|
#define KEY_TY CacheKey
|
||||||
|
#define VAL_TY CacheValue*
|
||||||
|
static uint64_t key_hash(KEY_TY k);
|
||||||
|
#define HASH_FN key_hash
|
||||||
|
static bool keys_are_equal(KEY_TY a, KEY_TY b);
|
||||||
|
#define CMPR_FN keys_are_equal
|
||||||
|
static void free_cache_value(CacheValue *cv) { free(cv->data); cv->data = NULL; free(cv); }
|
||||||
|
static void free_cache_key(CacheKey cv) { free(cv.hash_key); cv.hash_key = NULL; }
|
||||||
|
#define KEY_DTOR_FN free_cache_key
|
||||||
|
#define VAL_DTOR_FN free_cache_value
|
||||||
|
#include "kitty-verstable.h"
|
||||||
|
static uint64_t key_hash(CacheKey k) { return vt_hash_bytes(k.hash_key, k.hash_keylen); }
|
||||||
|
static bool keys_are_equal(CacheKey a, CacheKey b) { return a.hash_keylen == b.hash_keylen && memcmp(a.hash_key, b.hash_key, a.hash_keylen) == 0; }
|
||||||
|
|
||||||
typedef struct Hole {
|
typedef struct Hole {
|
||||||
off_t pos, size;
|
off_t pos, size;
|
||||||
@@ -52,18 +68,12 @@ typedef struct {
|
|||||||
pthread_t write_thread;
|
pthread_t write_thread;
|
||||||
bool thread_started, lock_inited, loop_data_inited, shutting_down, fully_initialized;
|
bool thread_started, lock_inited, loop_data_inited, shutting_down, fully_initialized;
|
||||||
LoopData loop_data;
|
LoopData loop_data;
|
||||||
CacheEntry *entries, currently_writing;
|
struct { CacheValue val; CacheKey key; } currently_writing;
|
||||||
|
cache_map map;
|
||||||
Holes holes;
|
Holes holes;
|
||||||
unsigned long long total_size;
|
unsigned long long total_size;
|
||||||
} DiskCache;
|
} DiskCache;
|
||||||
|
|
||||||
void
|
|
||||||
free_cache_entry(CacheEntry *e) {
|
|
||||||
if (e->hash_key) { free(e->hash_key); e->hash_key = NULL; }
|
|
||||||
if (e->data) { free(e->data); e->data = NULL; }
|
|
||||||
free(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define mutex(op) pthread_mutex_##op(&self->lock)
|
#define mutex(op) pthread_mutex_##op(&self->lock)
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
@@ -125,12 +135,18 @@ disk_cache_size_on_disk(PyObject *self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t hash_key[MAX_KEY_SIZE];
|
CacheKey key;
|
||||||
unsigned short hash_keylen;
|
|
||||||
off_t old_offset, new_offset;
|
off_t old_offset, new_offset;
|
||||||
size_t data_sz;
|
size_t data_sz;
|
||||||
} DefragEntry;
|
} DefragEntry;
|
||||||
|
|
||||||
|
static CacheKey
|
||||||
|
keydup(CacheKey k) {
|
||||||
|
CacheKey ans = {.hash_key=malloc(k.hash_keylen), .hash_keylen=k.hash_keylen};
|
||||||
|
if (ans.hash_key) memcpy(ans.hash_key, k.hash_key, k.hash_keylen);
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
defrag(DiskCache *self) {
|
defrag(DiskCache *self) {
|
||||||
int new_cache_file = -1;
|
int new_cache_file = -1;
|
||||||
@@ -140,7 +156,7 @@ defrag(DiskCache *self) {
|
|||||||
|
|
||||||
off_t size_on_disk = size_of_cache_file(self);
|
off_t size_on_disk = size_of_cache_file(self);
|
||||||
if (size_on_disk <= 0) goto cleanup;
|
if (size_on_disk <= 0) goto cleanup;
|
||||||
size_t num_entries = HASH_COUNT(self->entries);
|
size_t num_entries = vt_size(&self->map);
|
||||||
if (!num_entries) goto cleanup;
|
if (!num_entries) goto cleanup;
|
||||||
new_cache_file = open_cache_file(self->cache_dir);
|
new_cache_file = open_cache_file(self->cache_dir);
|
||||||
if (new_cache_file < 0) {
|
if (new_cache_file < 0) {
|
||||||
@@ -150,15 +166,15 @@ defrag(DiskCache *self) {
|
|||||||
defrag_entries = calloc(num_entries, sizeof(DefragEntry));
|
defrag_entries = calloc(num_entries, sizeof(DefragEntry));
|
||||||
if (!defrag_entries) goto cleanup;
|
if (!defrag_entries) goto cleanup;
|
||||||
size_t total_data_size = 0, num_entries_to_defrag = 0;
|
size_t total_data_size = 0, num_entries_to_defrag = 0;
|
||||||
CacheEntry *tmp, *s;
|
for (cache_map_itr i = vt_first(&self->map); !vt_is_end(i); i = vt_next(i)) {
|
||||||
HASH_ITER(hh, self->entries, s, tmp) {
|
CacheValue *s = i.data->val;
|
||||||
if (s->pos_in_cache_file > -1 && s->data_sz) {
|
if (s->pos_in_cache_file > -1 && s->data_sz) {
|
||||||
total_data_size += s->data_sz;
|
total_data_size += s->data_sz;
|
||||||
DefragEntry *e = defrag_entries + num_entries_to_defrag++;
|
DefragEntry *e = defrag_entries + num_entries_to_defrag++;
|
||||||
e->hash_keylen = s->hash_keylen;
|
|
||||||
e->old_offset = s->pos_in_cache_file;
|
e->old_offset = s->pos_in_cache_file;
|
||||||
e->data_sz = s->data_sz;
|
e->data_sz = s->data_sz;
|
||||||
if (s->hash_key) memcpy(e->hash_key, s->hash_key, s->hash_keylen);
|
e->key = keydup(i.data->key); // have to dup the key as we release the mutex and another thread might free the underlying key.
|
||||||
|
if (!e->key.hash_key) { fprintf(stderr, "Failed to allocate space for keydup in defrag\n"); goto cleanup; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ftruncate(new_cache_file, total_data_size) != 0) {
|
if (ftruncate(new_cache_file, total_data_size) != 0) {
|
||||||
@@ -190,9 +206,9 @@ cleanup:
|
|||||||
self->cache_file_fd = new_cache_file; new_cache_file = -1;
|
self->cache_file_fd = new_cache_file; new_cache_file = -1;
|
||||||
for (size_t i = 0; i < num_entries_to_defrag; i++) {
|
for (size_t i = 0; i < num_entries_to_defrag; i++) {
|
||||||
DefragEntry *e = defrag_entries + i;
|
DefragEntry *e = defrag_entries + i;
|
||||||
s = NULL;
|
cache_map_itr i = vt_get(&self->map, e->key);
|
||||||
HASH_FIND(hh, self->entries, e->hash_key, e->hash_keylen, s);
|
if (!vt_is_end(i)) i.data->val->pos_in_cache_file = e->new_offset;
|
||||||
if (s) s->pos_in_cache_file = e->new_offset;
|
free(e->key.hash_key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (new_cache_file > -1) safe_close(new_cache_file, __FILE__, __LINE__);
|
if (new_cache_file > -1) safe_close(new_cache_file, __FILE__, __LINE__);
|
||||||
@@ -208,7 +224,7 @@ find_hole_to_use(DiskCache *self, const off_t required_sz) {
|
|||||||
Hole *h = self->holes.items + i;
|
Hole *h = self->holes.items + i;
|
||||||
if (!found && h->size >= required_sz) {
|
if (!found && h->size >= required_sz) {
|
||||||
found = true;
|
found = true;
|
||||||
self->currently_writing.pos_in_cache_file = h->pos;
|
self->currently_writing.val.pos_in_cache_file = h->pos;
|
||||||
bool was_largest_hole = h->size == self->holes.largest_hole_size;
|
bool was_largest_hole = h->size == self->holes.largest_hole_size;
|
||||||
h->size -= required_sz;
|
h->size -= required_sz;
|
||||||
h->pos += required_sz;
|
h->pos += required_sz;
|
||||||
@@ -255,7 +271,7 @@ add_hole(DiskCache *self, off_t pos, off_t size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
remove_from_disk(DiskCache *self, CacheEntry *s) {
|
remove_from_disk(DiskCache *self, CacheValue *s) {
|
||||||
if (s->written_to_disk) {
|
if (s->written_to_disk) {
|
||||||
s->written_to_disk = false;
|
s->written_to_disk = false;
|
||||||
if (s->data_sz && s->pos_in_cache_file > -1) {
|
if (s->data_sz && s->pos_in_cache_file > -1) {
|
||||||
@@ -267,20 +283,20 @@ remove_from_disk(DiskCache *self, CacheEntry *s) {
|
|||||||
|
|
||||||
static bool
|
static bool
|
||||||
find_cache_entry_to_write(DiskCache *self) {
|
find_cache_entry_to_write(DiskCache *self) {
|
||||||
CacheEntry *tmp, *s;
|
|
||||||
if (needs_defrag(self)) defrag(self);
|
if (needs_defrag(self)) defrag(self);
|
||||||
HASH_ITER(hh, self->entries, s, tmp) {
|
for (cache_map_itr i = vt_first(&self->map); !vt_is_end(i); i = vt_next(i)) {
|
||||||
|
CacheValue *s = i.data->val;
|
||||||
if (!s->written_to_disk) {
|
if (!s->written_to_disk) {
|
||||||
if (s->data) {
|
if (s->data) {
|
||||||
if (self->currently_writing.data) free(self->currently_writing.data);
|
if (self->currently_writing.val.data) free(self->currently_writing.val.data);
|
||||||
self->currently_writing.data = s->data;
|
self->currently_writing.val.data = s->data;
|
||||||
s->data = NULL;
|
s->data = NULL;
|
||||||
self->currently_writing.data_sz = s->data_sz;
|
self->currently_writing.val.data_sz = s->data_sz;
|
||||||
self->currently_writing.pos_in_cache_file = -1;
|
self->currently_writing.val.pos_in_cache_file = -1;
|
||||||
xor_data64(s->encryption_key, self->currently_writing.data, s->data_sz);
|
xor_data64(s->encryption_key, self->currently_writing.val.data, s->data_sz);
|
||||||
self->currently_writing.hash_keylen = MIN(s->hash_keylen, MAX_KEY_SIZE);
|
self->currently_writing.key.hash_keylen = MIN(i.data->key.hash_keylen, MAX_KEY_SIZE);
|
||||||
memcpy(self->currently_writing.hash_key, s->hash_key, self->currently_writing.hash_keylen);
|
memcpy(self->currently_writing.key.hash_key, i.data->key.hash_key, self->currently_writing.key.hash_keylen);
|
||||||
find_hole_to_use(self, self->currently_writing.data_sz);
|
find_hole_to_use(self, self->currently_writing.val.data_sz);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
s->written_to_disk = true;
|
s->written_to_disk = true;
|
||||||
@@ -293,27 +309,27 @@ find_cache_entry_to_write(DiskCache *self) {
|
|||||||
|
|
||||||
static bool
|
static bool
|
||||||
write_dirty_entry(DiskCache *self) {
|
write_dirty_entry(DiskCache *self) {
|
||||||
size_t left = self->currently_writing.data_sz;
|
size_t left = self->currently_writing.val.data_sz;
|
||||||
uint8_t *p = self->currently_writing.data;
|
uint8_t *p = self->currently_writing.val.data;
|
||||||
if (self->currently_writing.pos_in_cache_file < 0) {
|
if (self->currently_writing.val.pos_in_cache_file < 0) {
|
||||||
self->currently_writing.pos_in_cache_file = size_of_cache_file(self);
|
self->currently_writing.val.pos_in_cache_file = size_of_cache_file(self);
|
||||||
if (self->currently_writing.pos_in_cache_file < 0) {
|
if (self->currently_writing.val.pos_in_cache_file < 0) {
|
||||||
perror("Failed to seek in disk cache file");
|
perror("Failed to seek in disk cache file");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
off_t offset = self->currently_writing.pos_in_cache_file;
|
off_t offset = self->currently_writing.val.pos_in_cache_file;
|
||||||
while (left > 0) {
|
while (left > 0) {
|
||||||
ssize_t n = pwrite(self->cache_file_fd, p, left, offset);
|
ssize_t n = pwrite(self->cache_file_fd, p, left, offset);
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
if (errno == EINTR || errno == EAGAIN) continue;
|
if (errno == EINTR || errno == EAGAIN) continue;
|
||||||
perror("Failed to write to disk-cache file");
|
perror("Failed to write to disk-cache file");
|
||||||
self->currently_writing.pos_in_cache_file = -1;
|
self->currently_writing.val.pos_in_cache_file = -1;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
fprintf(stderr, "Failed to write to disk-cache file with zero return\n");
|
fprintf(stderr, "Failed to write to disk-cache file with zero return\n");
|
||||||
self->currently_writing.pos_in_cache_file = -1;
|
self->currently_writing.val.pos_in_cache_file = -1;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
left -= n;
|
left -= n;
|
||||||
@@ -325,15 +341,14 @@ write_dirty_entry(DiskCache *self) {
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
retire_currently_writing(DiskCache *self) {
|
retire_currently_writing(DiskCache *self) {
|
||||||
CacheEntry *s = NULL;
|
cache_map_itr i = vt_get(&self->map, self->currently_writing.key);
|
||||||
HASH_FIND(hh, self->entries, self->currently_writing.hash_key, self->currently_writing.hash_keylen, s);
|
if (!vt_is_end(i)) {
|
||||||
if (s) {
|
i.data->val->written_to_disk = true;
|
||||||
s->written_to_disk = true;
|
i.data->val->pos_in_cache_file = self->currently_writing.val.pos_in_cache_file;
|
||||||
s->pos_in_cache_file = self->currently_writing.pos_in_cache_file;
|
|
||||||
}
|
}
|
||||||
free(self->currently_writing.data);
|
free(self->currently_writing.val.data);
|
||||||
self->currently_writing.data = NULL;
|
self->currently_writing.val.data = NULL;
|
||||||
self->currently_writing.data_sz = 0;
|
self->currently_writing.val.data_sz = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void*
|
static void*
|
||||||
@@ -348,7 +363,7 @@ write_loop(void *data) {
|
|||||||
while (!self->shutting_down) {
|
while (!self->shutting_down) {
|
||||||
mutex(lock);
|
mutex(lock);
|
||||||
found_dirty_entry = find_cache_entry_to_write(self);
|
found_dirty_entry = find_cache_entry_to_write(self);
|
||||||
size_t count = HASH_COUNT(self->entries);
|
size_t count = vt_size(&self->map);
|
||||||
mutex(unlock);
|
mutex(unlock);
|
||||||
if (found_dirty_entry) {
|
if (found_dirty_entry) {
|
||||||
write_dirty_entry(self);
|
write_dirty_entry(self);
|
||||||
@@ -380,9 +395,9 @@ ensure_state(DiskCache *self) {
|
|||||||
if (!init_loop_data(&self->loop_data, 0)) { PyErr_SetFromErrno(PyExc_OSError); return false; }
|
if (!init_loop_data(&self->loop_data, 0)) { PyErr_SetFromErrno(PyExc_OSError); return false; }
|
||||||
self->loop_data_inited = true;
|
self->loop_data_inited = true;
|
||||||
}
|
}
|
||||||
if (!self->currently_writing.hash_key) {
|
if (!self->currently_writing.key.hash_key) {
|
||||||
self->currently_writing.hash_key = malloc(MAX_KEY_SIZE);
|
self->currently_writing.key.hash_key = malloc(MAX_KEY_SIZE);
|
||||||
if (!self->currently_writing.hash_key) { PyErr_NoMemory(); return false; }
|
if (!self->currently_writing.key.hash_key) { PyErr_NoMemory(); return false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!self->lock_inited) {
|
if (!self->lock_inited) {
|
||||||
@@ -424,7 +439,7 @@ ensure_state(DiskCache *self) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
vt_init(&self->map);
|
||||||
self->fully_initialized = true;
|
self->fully_initialized = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -442,8 +457,8 @@ dealloc(DiskCache* self) {
|
|||||||
pthread_join(self->write_thread, NULL);
|
pthread_join(self->write_thread, NULL);
|
||||||
self->thread_started = false;
|
self->thread_started = false;
|
||||||
}
|
}
|
||||||
if (self->currently_writing.hash_key) {
|
if (self->currently_writing.key.hash_key) {
|
||||||
free(self->currently_writing.hash_key); self->currently_writing.hash_key = NULL;
|
free(self->currently_writing.key.hash_key); self->currently_writing.key.hash_key = NULL;
|
||||||
}
|
}
|
||||||
if (self->lock_inited) {
|
if (self->lock_inited) {
|
||||||
pthread_mutex_destroy(&self->lock);
|
pthread_mutex_destroy(&self->lock);
|
||||||
@@ -453,33 +468,22 @@ dealloc(DiskCache* self) {
|
|||||||
free_loop_data(&self->loop_data);
|
free_loop_data(&self->loop_data);
|
||||||
self->loop_data_inited = false;
|
self->loop_data_inited = false;
|
||||||
}
|
}
|
||||||
if (self->entries) {
|
vt_cleanup(&self->map);
|
||||||
CacheEntry *tmp, *s;
|
|
||||||
HASH_ITER(hh, self->entries, s, tmp) {
|
|
||||||
HASH_DEL(self->entries, s);
|
|
||||||
free_cache_entry(s); s = NULL;
|
|
||||||
}
|
|
||||||
self->entries = NULL;
|
|
||||||
}
|
|
||||||
if (self->cache_file_fd > -1) {
|
if (self->cache_file_fd > -1) {
|
||||||
safe_close(self->cache_file_fd, __FILE__, __LINE__);
|
safe_close(self->cache_file_fd, __FILE__, __LINE__);
|
||||||
self->cache_file_fd = -1;
|
self->cache_file_fd = -1;
|
||||||
}
|
}
|
||||||
free(self->holes.items); zero_at_ptr(&self->holes);
|
free(self->holes.items); zero_at_ptr(&self->holes);
|
||||||
if (self->currently_writing.data) free(self->currently_writing.data);
|
if (self->currently_writing.val.data) free(self->currently_writing.val.data);
|
||||||
free(self->cache_dir); self->cache_dir = NULL;
|
free(self->cache_dir); self->cache_dir = NULL;
|
||||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static CacheEntry*
|
static CacheValue*
|
||||||
create_cache_entry(const void *key, const size_t key_sz) {
|
create_cache_entry(void) {
|
||||||
CacheEntry *s = calloc(1, sizeof(CacheEntry));
|
CacheValue *s = calloc(1, sizeof(CacheValue));
|
||||||
if (!s) return (CacheEntry*)PyErr_NoMemory();
|
if (!s) return (CacheValue*)PyErr_NoMemory();
|
||||||
if (!secure_random_bytes(s->encryption_key, sizeof(s->encryption_key))) { free(s); PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
if (!secure_random_bytes(s->encryption_key, sizeof(s->encryption_key))) { free(s); PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
||||||
s->hash_key = malloc(key_sz);
|
|
||||||
if (!s->hash_key) { free(s); PyErr_NoMemory(); return NULL; }
|
|
||||||
s->hash_keylen = key_sz;
|
|
||||||
memcpy(s->hash_key, key, key_sz);
|
|
||||||
s->pos_in_cache_file = -2;
|
s->pos_in_cache_file = -2;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@@ -489,17 +493,22 @@ add_to_disk_cache(PyObject *self_, const void *key, size_t key_sz, const void *d
|
|||||||
DiskCache *self = (DiskCache*)self_;
|
DiskCache *self = (DiskCache*)self_;
|
||||||
if (!ensure_state(self)) return false;
|
if (!ensure_state(self)) return false;
|
||||||
if (key_sz > MAX_KEY_SIZE) { PyErr_SetString(PyExc_KeyError, "cache key is too long"); return false; }
|
if (key_sz > MAX_KEY_SIZE) { PyErr_SetString(PyExc_KeyError, "cache key is too long"); return false; }
|
||||||
CacheEntry *s = NULL;
|
|
||||||
RAII_ALLOC(uint8_t, copied_data, malloc(data_sz));
|
RAII_ALLOC(uint8_t, copied_data, malloc(data_sz));
|
||||||
if (!copied_data) { PyErr_NoMemory(); return false; }
|
if (!copied_data) { PyErr_NoMemory(); return false; }
|
||||||
memcpy(copied_data, data, data_sz);
|
memcpy(copied_data, data, data_sz);
|
||||||
|
CacheKey k = {.hash_key=(void*)key, .hash_keylen=key_sz};
|
||||||
|
|
||||||
mutex(lock);
|
mutex(lock);
|
||||||
HASH_FIND(hh, self->entries, key, key_sz, s);
|
cache_map_itr i = vt_get(&self->map, k);
|
||||||
if (s == NULL) {
|
CacheValue *s;
|
||||||
if (!(s = create_cache_entry(key, key_sz))) goto end;
|
if (vt_is_end(i)) {
|
||||||
HASH_ADD_KEYPTR(hh, self->entries, s->hash_key, s->hash_keylen, s);
|
k.hash_key = malloc(key_sz);
|
||||||
|
if (!k.hash_key) { PyErr_NoMemory(); goto end; }
|
||||||
|
memcpy(k.hash_key, key, key_sz);
|
||||||
|
if (!(s = create_cache_entry())) goto end;
|
||||||
|
if (vt_is_end(vt_insert(&self->map, k, s))) { PyErr_NoMemory(); goto end; }
|
||||||
} else {
|
} else {
|
||||||
|
s = i.data->val;
|
||||||
remove_from_disk(self, s);
|
remove_from_disk(self, s);
|
||||||
self->total_size -= MIN(self->total_size, s->data_sz);
|
self->total_size -= MIN(self->total_size, s->data_sz);
|
||||||
if (s->data) free(s->data);
|
if (s->data) free(s->data);
|
||||||
@@ -518,17 +527,18 @@ remove_from_disk_cache(PyObject *self_, const void *key, size_t key_sz) {
|
|||||||
DiskCache *self = (DiskCache*)self_;
|
DiskCache *self = (DiskCache*)self_;
|
||||||
if (!ensure_state(self)) return false;
|
if (!ensure_state(self)) return false;
|
||||||
if (key_sz > MAX_KEY_SIZE) { PyErr_SetString(PyExc_KeyError, "cache key is too long"); return false; }
|
if (key_sz > MAX_KEY_SIZE) { PyErr_SetString(PyExc_KeyError, "cache key is too long"); return false; }
|
||||||
CacheEntry *s = NULL;
|
CacheValue *s = NULL;
|
||||||
|
CacheKey k = {.hash_key=(void*)key, .hash_keylen=key_sz};
|
||||||
bool removed = false;
|
bool removed = false;
|
||||||
|
|
||||||
mutex(lock);
|
mutex(lock);
|
||||||
HASH_FIND(hh, self->entries, key, key_sz, s);
|
cache_map_itr i = vt_get(&self->map, k);
|
||||||
if (s) {
|
if (!vt_is_end(i)) {
|
||||||
removed = true;
|
removed = true;
|
||||||
HASH_DEL(self->entries, s);
|
s = i.data->val;
|
||||||
remove_from_disk(self, s);
|
remove_from_disk(self, s);
|
||||||
self->total_size = (self->total_size > s->data_sz) ? self->total_size - s->data_sz : 0;
|
self->total_size = (self->total_size > s->data_sz) ? self->total_size - s->data_sz : 0;
|
||||||
free_cache_entry(s);
|
vt_erase_itr(&self->map, i);
|
||||||
}
|
}
|
||||||
mutex(unlock);
|
mutex(unlock);
|
||||||
wakeup_write_loop(self);
|
wakeup_write_loop(self);
|
||||||
@@ -539,12 +549,8 @@ void
|
|||||||
clear_disk_cache(PyObject *self_) {
|
clear_disk_cache(PyObject *self_) {
|
||||||
DiskCache *self = (DiskCache*)self_;
|
DiskCache *self = (DiskCache*)self_;
|
||||||
if (!ensure_state(self)) return;
|
if (!ensure_state(self)) return;
|
||||||
CacheEntry *s, *tmp;
|
|
||||||
mutex(lock);
|
mutex(lock);
|
||||||
HASH_ITER(hh, self->entries, s, tmp) {
|
vt_cleanup(&self->map);
|
||||||
HASH_DEL(self->entries, s);
|
|
||||||
free_cache_entry(s);
|
|
||||||
}
|
|
||||||
self->total_size = 0;
|
self->total_size = 0;
|
||||||
self->holes.count = 0;
|
self->holes.count = 0;
|
||||||
self->holes.largest_hole_size = 0;
|
self->holes.largest_hole_size = 0;
|
||||||
@@ -577,7 +583,7 @@ read_from_cache_file(const DiskCache *self, off_t pos, size_t sz, void *dest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
read_from_cache_entry(const DiskCache *self, const CacheEntry *s, void *dest) {
|
read_from_cache_entry(const DiskCache *self, const CacheValue *s, void *dest) {
|
||||||
size_t sz = s->data_sz;
|
size_t sz = s->data_sz;
|
||||||
off_t pos = s->pos_in_cache_file;
|
off_t pos = s->pos_in_cache_file;
|
||||||
if (pos < 0) {
|
if (pos < 0) {
|
||||||
@@ -593,17 +599,18 @@ read_from_disk_cache(PyObject *self_, const void *key, size_t key_sz, void*(allo
|
|||||||
void *data = NULL;
|
void *data = NULL;
|
||||||
if (!ensure_state(self)) return data;
|
if (!ensure_state(self)) return data;
|
||||||
if (key_sz > MAX_KEY_SIZE) { PyErr_SetString(PyExc_KeyError, "cache key is too long"); return data; }
|
if (key_sz > MAX_KEY_SIZE) { PyErr_SetString(PyExc_KeyError, "cache key is too long"); return data; }
|
||||||
|
CacheKey k = {.hash_key=(void*)key, .hash_keylen=key_sz};
|
||||||
|
|
||||||
mutex(lock);
|
mutex(lock);
|
||||||
CacheEntry *s = NULL;
|
cache_map_itr i = vt_get(&self->map, k);
|
||||||
HASH_FIND(hh, self->entries, key, key_sz, s);
|
if (vt_is_end(i)) { PyErr_SetString(PyExc_KeyError, "No cached entry with specified key found"); goto end; }
|
||||||
if (!s) { PyErr_SetString(PyExc_KeyError, "No cached entry with specified key found"); goto end; }
|
CacheValue *s = i.data->val;
|
||||||
data = allocator(allocator_data, s->data_sz);
|
data = allocator(allocator_data, s->data_sz);
|
||||||
if (!data) { PyErr_NoMemory(); goto end; }
|
if (!data) { PyErr_NoMemory(); goto end; }
|
||||||
|
|
||||||
if (s->data) { memcpy(data, s->data, s->data_sz); }
|
if (s->data) { memcpy(data, s->data, s->data_sz); }
|
||||||
else if (self->currently_writing.data && self->currently_writing.hash_key && self->currently_writing.hash_keylen == key_sz && memcmp(self->currently_writing.hash_key, key, key_sz) == 0) {
|
else if (self->currently_writing.val.data && self->currently_writing.key.hash_key && keys_are_equal(self->currently_writing.key, k)) {
|
||||||
memcpy(data, self->currently_writing.data, s->data_sz);
|
memcpy(data, self->currently_writing.val.data, s->data_sz);
|
||||||
xor_data64(s->encryption_key, data, s->data_sz);
|
xor_data64(s->encryption_key, data, s->data_sz);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -627,9 +634,9 @@ disk_cache_clear_from_ram(PyObject *self_, bool(matches)(void*, void *key, unsig
|
|||||||
size_t ans = 0;
|
size_t ans = 0;
|
||||||
if (!ensure_state(self)) return ans;
|
if (!ensure_state(self)) return ans;
|
||||||
mutex(lock);
|
mutex(lock);
|
||||||
CacheEntry *s, *tmp;
|
for (cache_map_itr i = vt_first(&self->map); !vt_is_end(i); i = vt_next(i)) {
|
||||||
HASH_ITER(hh, self->entries, s, tmp) {
|
CacheValue *s = i.data->val;
|
||||||
if (s->written_to_disk && s->data && matches(data, s->hash_key, s->hash_keylen)) {
|
if (s->written_to_disk && s->data && matches(data, i.data->key.hash_key, i.data->key.hash_keylen)) {
|
||||||
free(s->data); s->data = NULL;
|
free(s->data); s->data = NULL;
|
||||||
ans++;
|
ans++;
|
||||||
}
|
}
|
||||||
@@ -646,9 +653,8 @@ disk_cache_wait_for_write(PyObject *self_, monotonic_t timeout) {
|
|||||||
while (!timeout || monotonic() <= end_at) {
|
while (!timeout || monotonic() <= end_at) {
|
||||||
bool pending = false;
|
bool pending = false;
|
||||||
mutex(lock);
|
mutex(lock);
|
||||||
CacheEntry *s, *tmp;
|
for (cache_map_itr i = vt_first(&self->map); !vt_is_end(i); i = vt_next(i)) {
|
||||||
HASH_ITER(hh, self->entries, s, tmp) {
|
if (!i.data->val->written_to_disk) {
|
||||||
if (!s->written_to_disk) {
|
|
||||||
pending = true;
|
pending = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -671,9 +677,8 @@ disk_cache_num_cached_in_ram(PyObject *self_) {
|
|||||||
unsigned long ans = 0;
|
unsigned long ans = 0;
|
||||||
if (ensure_state(self)) {
|
if (ensure_state(self)) {
|
||||||
mutex(lock);
|
mutex(lock);
|
||||||
CacheEntry *tmp, *s;
|
for (cache_map_itr i = vt_first(&self->map); !vt_is_end(i); i = vt_next(i)) {
|
||||||
HASH_ITER(hh, self->entries, s, tmp) {
|
if (i.data->val->written_to_disk && i.data->val->data) ans++;
|
||||||
if (s->written_to_disk && s->data) ans++;
|
|
||||||
}
|
}
|
||||||
mutex(unlock);
|
mutex(unlock);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user