Start work on disk cache

This commit is contained in:
Kovid Goyal 2020-12-29 19:16:21 +05:30
parent e2d61ab5be
commit 3162d391cc
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
7 changed files with 175 additions and 6 deletions

View File

@ -25,8 +25,7 @@ from .config import (
)
from .config_data import MINIMUM_FONT_SIZE
from .constants import (
appname, cache_dir, config_dir, is_macos, kitty_exe,
supports_primary_selection
appname, config_dir, is_macos, kitty_exe, supports_primary_selection
)
from .fast_data_types import (
CLOSE_BEING_CONFIRMED, IMPERATIVE_CLOSE_REQUESTED, NO_CLOSE_REQUESTED,
@ -1380,10 +1379,6 @@ class Boss:
with suppress(FileNotFoundError):
os.remove(path)
def create_temp_dir_in_cache(self, prefix: Optional[str] = None, suffix: Optional[str] = None) -> str:
from tempfile import mkdtemp
return mkdtemp(prefix=prefix, suffix=suffix, dir=cache_dir())
def set_update_check_process(self, process: Optional[PopenType] = None) -> None:
if self.update_check_process is not None:
with suppress(Exception):

View File

@ -128,6 +128,9 @@ defconf = os.path.join(config_dir, 'kitty.conf')
@lru_cache(maxsize=2)
def cache_dir() -> str:
override: Optional[str] = getattr(cache_dir, 'override_dir', None)
if override:
return override
if 'KITTY_CACHE_DIRECTORY' in os.environ:
candidate = os.path.abspath(os.environ['KITTY_CACHE_DIRECTORY'])
elif is_macos:
@ -139,6 +142,13 @@ def cache_dir() -> str:
return candidate
def dir_for_disk_cache() -> str:
from tempfile import mkdtemp
ans = os.path.join(cache_dir(), 'disk-cache')
os.makedirs(ans, exist_ok=True)
return mkdtemp(dir=ans)
def wakeup() -> None:
from .fast_data_types import get_boss
b = get_boss()

View File

@ -192,6 +192,7 @@ static struct PyModuleDef module = {
extern int init_LineBuf(PyObject *);
extern int init_HistoryBuf(PyObject *);
extern int init_Cursor(PyObject *);
extern int init_DiskCache(PyObject *);
extern bool init_child_monitor(PyObject *);
extern int init_Line(PyObject *);
extern int init_ColorProfile(PyObject *);
@ -231,6 +232,7 @@ PyInit_fast_data_types(void) {
if (!init_HistoryBuf(m)) return NULL;
if (!init_Line(m)) return NULL;
if (!init_Cursor(m)) return NULL;
if (!init_DiskCache(m)) return NULL;
if (!init_child_monitor(m)) return NULL;
if (!init_ColorProfile(m)) return NULL;
if (!init_Screen(m)) return NULL;

146
kitty/disk-cache.c Normal file
View File

@ -0,0 +1,146 @@
/*
* disk-cache.c
* Copyright (C) 2020 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#include "disk-cache.h"
#include "state.h"
#include "loop-utils.h"
#include <stdlib.h>
typedef struct {
PyObject_HEAD
char *path;
pthread_mutex_t lock;
pthread_t write_thread;
bool thread_started, lock_inited, loop_data_inited, shutting_down, fully_initialized;
LoopData loop_data;
PyObject *rmtree;
} DiskCache;
#define mutex(op) pthread_mutex_##op(&self->lock)
static PyObject*
new(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
DiskCache *self;
self = (DiskCache*)type->tp_alloc(type, 0);
if (self) {
PyObject *shutil = PyImport_ImportModule("shutil");
if (!shutil) { Py_CLEAR(self); return NULL; }
self->rmtree = PyObject_GetAttrString(shutil, "rmtree");
Py_CLEAR(shutil);
if (!self->rmtree) { Py_CLEAR(self); return NULL; }
}
return (PyObject*) self;
}
static void*
write_loop(void *data) {
DiskCache *self = (DiskCache*)data;
while (!self->shutting_down) {
}
return 0;
}
static bool
ensure_state(DiskCache *self) {
int ret;
if (self->fully_initialized) return true;
if (!self->loop_data_inited) {
if (!init_loop_data(&self->loop_data)) { PyErr_SetFromErrno(PyExc_OSError); return false; }
self->loop_data_inited = true;
}
if (!self->lock_inited) {
if ((ret = pthread_mutex_init(&self->lock, NULL)) != 0) {
PyErr_Format(PyExc_OSError, "Failed to create disk cache lock mutex: %s", strerror(ret));
return false;
}
self->lock_inited = true;
}
if (!self->thread_started) {
if ((ret = pthread_create(&self->write_thread, NULL, write_loop, self)) != 0) {
PyErr_Format(PyExc_OSError, "Failed to start disk cache write thread with error: %s", strerror(ret));
return false;
}
self->thread_started = true;
}
if (!self->path) {
PyObject *kc = NULL, *cache_dir = NULL;
kc = PyImport_ImportModule("kitty.constants");
if (kc) {
cache_dir = PyObject_CallMethod(kc, "dir_for_disk_cache", NULL);
if (cache_dir) {
self->path = strdup(PyUnicode_AsUTF8(cache_dir));
if (!self->path) PyErr_NoMemory();
}
}
Py_CLEAR(kc); Py_CLEAR(cache_dir);
if (PyErr_Occurred()) return false;
}
self->fully_initialized = true;
return true;
}
static void
wakeup_write_loop(DiskCache *self) {
if (self->thread_started) wakeup_loop(&self->loop_data, false, "disk_cache_write_loop");
}
static void
dealloc(DiskCache* self) {
self->shutting_down = true;
if (self->thread_started) {
wakeup_write_loop(self);
pthread_join(self->write_thread, NULL);
self->thread_started = false;
}
if (self->lock_inited) {
pthread_mutex_destroy(&self->lock);
self->lock_inited = false;
}
if (self->loop_data_inited) {
free_loop_data(&self->loop_data);
self->loop_data_inited = false;
}
if (self->path) {
PyObject_CallFunction(self->rmtree, "sO", self->path, Py_True);
free(self->path); self->path = NULL;
}
Py_CLEAR(self->rmtree);
Py_TYPE(self)->tp_free((PyObject*)self);
}
#define PYWRAP0(name) static PyObject* py##name(DiskCache *self, PyObject *args UNUSED)
PYWRAP0(ensure_state) {
ensure_state(self);
Py_RETURN_NONE;
}
#define MW(name, arg_type) {#name, (PyCFunction)py##name, arg_type, NULL}
static PyMethodDef methods[] = {
MW(ensure_state, METH_NOARGS),
{NULL} /* Sentinel */
};
PyTypeObject DiskCache_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fast_data_types.DiskCache",
.tp_basicsize = sizeof(DiskCache),
.tp_dealloc = (destructor)dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "A disk based secure cache",
.tp_methods = methods,
.tp_new = new,
};
INIT_TYPE(DiskCache)
PyObject* create_disk_cache(void) { return new(&DiskCache_Type, NULL, NULL); }

11
kitty/disk-cache.h Normal file
View File

@ -0,0 +1,11 @@
/*
* Copyright (C) 2020 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#pragma once
#include <Python.h>
PyObject* create_disk_cache(void);

View File

@ -7,6 +7,7 @@
#include "graphics.h"
#include "state.h"
#include "disk-cache.h"
#include <sys/types.h>
#include <sys/stat.h>
@ -36,6 +37,8 @@ grman_alloc() {
PyErr_NoMemory();
Py_CLEAR(self); return NULL;
}
self->disk_cache = create_disk_cache();
if (!self->disk_cache) { Py_CLEAR(self); return NULL; }
return self;
}
@ -71,6 +74,7 @@ dealloc(GraphicsManager* self) {
free(self->images);
}
free(self->render_data);
Py_CLEAR(self->disk_cache);
Py_TYPE(self)->tp_free((PyObject*)self);
}

View File

@ -84,6 +84,7 @@ typedef struct {
size_t num_of_below_refs, num_of_negative_refs, num_of_positive_refs;
unsigned int last_scrolled_by;
size_t used_storage;
PyObject *disk_cache;
} GraphicsManager;