summaryrefslogtreecommitdiff
path: root/src/mwindow.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mwindow.c')
-rw-r--r--src/mwindow.c541
1 files changed, 0 insertions, 541 deletions
diff --git a/src/mwindow.c b/src/mwindow.c
deleted file mode 100644
index da2dae2b9..000000000
--- a/src/mwindow.c
+++ /dev/null
@@ -1,541 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "mwindow.h"
-
-#include "vector.h"
-#include "futils.h"
-#include "map.h"
-#include "runtime.h"
-#include "strmap.h"
-#include "pack.h"
-
-#define DEFAULT_WINDOW_SIZE \
- (sizeof(void*) >= 8 \
- ? 1 * 1024 * 1024 * 1024 \
- : 32 * 1024 * 1024)
-
-#define DEFAULT_MAPPED_LIMIT \
- ((1024 * 1024) * (sizeof(void*) >= 8 ? UINT64_C(8192) : UINT64_C(256)))
-
-/* default is unlimited */
-#define DEFAULT_FILE_LIMIT 0
-
-size_t git_mwindow__window_size = DEFAULT_WINDOW_SIZE;
-size_t git_mwindow__mapped_limit = DEFAULT_MAPPED_LIMIT;
-size_t git_mwindow__file_limit = DEFAULT_FILE_LIMIT;
-
-/* Mutex to control access to `git_mwindow__mem_ctl` and `git__pack_cache`. */
-git_mutex git__mwindow_mutex;
-
-/* Whenever you want to read or modify this, grab `git__mwindow_mutex` */
-git_mwindow_ctl git_mwindow__mem_ctl;
-
-/* Global list of mwindow files, to open packs once across repos */
-git_strmap *git__pack_cache = NULL;
-
-static void git_mwindow_global_shutdown(void)
-{
- git_strmap *tmp = git__pack_cache;
-
- git_mutex_free(&git__mwindow_mutex);
-
- git__pack_cache = NULL;
- git_strmap_free(tmp);
-}
-
-int git_mwindow_global_init(void)
-{
- int error;
-
- GIT_ASSERT(!git__pack_cache);
-
- if ((error = git_mutex_init(&git__mwindow_mutex)) < 0 ||
- (error = git_strmap_new(&git__pack_cache)) < 0)
- return error;
-
- return git_runtime_shutdown_register(git_mwindow_global_shutdown);
-}
-
-int git_mwindow_get_pack(struct git_pack_file **out, const char *path)
-{
- struct git_pack_file *pack;
- char *packname;
- int error;
-
- if ((error = git_packfile__name(&packname, path)) < 0)
- return error;
-
- if (git_mutex_lock(&git__mwindow_mutex) < 0) {
- git_error_set(GIT_ERROR_OS, "failed to lock mwindow mutex");
- return -1;
- }
-
- pack = git_strmap_get(git__pack_cache, packname);
- git__free(packname);
-
- if (pack != NULL) {
- git_atomic32_inc(&pack->refcount);
- git_mutex_unlock(&git__mwindow_mutex);
- *out = pack;
- return 0;
- }
-
- /* If we didn't find it, we need to create it */
- if ((error = git_packfile_alloc(&pack, path)) < 0) {
- git_mutex_unlock(&git__mwindow_mutex);
- return error;
- }
-
- git_atomic32_inc(&pack->refcount);
-
- error = git_strmap_set(git__pack_cache, pack->pack_name, pack);
- git_mutex_unlock(&git__mwindow_mutex);
- if (error < 0) {
- git_packfile_free(pack, false);
- return error;
- }
-
- *out = pack;
- return 0;
-}
-
-int git_mwindow_put_pack(struct git_pack_file *pack)
-{
- int count, error;
- struct git_pack_file *pack_to_delete = NULL;
-
- if ((error = git_mutex_lock(&git__mwindow_mutex)) < 0)
- return error;
-
- /* put before get would be a corrupted state */
- GIT_ASSERT(git__pack_cache);
-
- /* if we cannot find it, the state is corrupted */
- GIT_ASSERT(git_strmap_exists(git__pack_cache, pack->pack_name));
-
- count = git_atomic32_dec(&pack->refcount);
- if (count == 0) {
- git_strmap_delete(git__pack_cache, pack->pack_name);
- pack_to_delete = pack;
- }
- git_mutex_unlock(&git__mwindow_mutex);
- git_packfile_free(pack_to_delete, false);
-
- return 0;
-}
-
-/*
- * Free all the windows in a sequence, typically because we're done
- * with the file. Needs to hold the git__mwindow_mutex.
- */
-static int git_mwindow_free_all_locked(git_mwindow_file *mwf)
-{
- git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
- size_t i;
-
- /*
- * Remove these windows from the global list
- */
- for (i = 0; i < ctl->windowfiles.length; ++i){
- if (git_vector_get(&ctl->windowfiles, i) == mwf) {
- git_vector_remove(&ctl->windowfiles, i);
- break;
- }
- }
-
- if (ctl->windowfiles.length == 0) {
- git_vector_free(&ctl->windowfiles);
- ctl->windowfiles.contents = NULL;
- }
-
- while (mwf->windows) {
- git_mwindow *w = mwf->windows;
- GIT_ASSERT(w->inuse_cnt == 0);
-
- ctl->mapped -= w->window_map.len;
- ctl->open_windows--;
-
- git_futils_mmap_free(&w->window_map);
-
- mwf->windows = w->next;
- git__free(w);
- }
-
- return 0;
-}
-
-int git_mwindow_free_all(git_mwindow_file *mwf)
-{
- int error;
-
- if (git_mutex_lock(&git__mwindow_mutex)) {
- git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex");
- return -1;
- }
-
- error = git_mwindow_free_all_locked(mwf);
-
- git_mutex_unlock(&git__mwindow_mutex);
-
- return error;
-}
-
-/*
- * Check if a window 'win' contains the address 'offset'
- */
-int git_mwindow_contains(git_mwindow *win, off64_t offset)
-{
- off64_t win_off = win->offset;
- return win_off <= offset
- && offset <= (off64_t)(win_off + win->window_map.len);
-}
-
-#define GIT_MWINDOW__LRU -1
-#define GIT_MWINDOW__MRU 1
-
-/*
- * Find the least- or most-recently-used window in a file that is not currently
- * being used. The 'only_unused' flag controls whether the caller requires the
- * file to only have unused windows. If '*out_window' is non-null, it is used as
- * a starting point for the comparison.
- *
- * Returns whether such a window was found in the file.
- */
-static bool git_mwindow_scan_recently_used(
- git_mwindow_file *mwf,
- git_mwindow **out_window,
- git_mwindow **out_last,
- bool only_unused,
- int comparison_sign)
-{
- git_mwindow *w, *w_last;
- git_mwindow *lru_window = NULL, *lru_last = NULL;
- bool found = false;
-
- GIT_ASSERT_ARG(mwf);
- GIT_ASSERT_ARG(out_window);
-
- lru_window = *out_window;
- if (out_last)
- lru_last = *out_last;
-
- for (w_last = NULL, w = mwf->windows; w; w_last = w, w = w->next) {
- if (w->inuse_cnt) {
- if (only_unused)
- return false;
- /* This window is currently being used. Skip it. */
- continue;
- }
-
- /*
- * If the current one is more (or less) recent than the last one,
- * store it in the output parameter. If lru_window is NULL,
- * it's the first loop, so store it as well.
- */
- if (!lru_window ||
- (comparison_sign == GIT_MWINDOW__LRU && lru_window->last_used > w->last_used) ||
- (comparison_sign == GIT_MWINDOW__MRU && lru_window->last_used < w->last_used)) {
- lru_window = w;
- lru_last = w_last;
- found = true;
- }
- }
-
- if (!found)
- return false;
-
- *out_window = lru_window;
- if (out_last)
- *out_last = lru_last;
- return true;
-}
-
-/*
- * Close the least recently used window (that is currently not being used) out
- * of all the files. Called under lock from new_window_locked.
- */
-static int git_mwindow_close_lru_window_locked(void)
-{
- git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
- git_mwindow_file *cur;
- size_t i;
- git_mwindow *lru_window = NULL, *lru_last = NULL, **list = NULL;
-
- git_vector_foreach(&ctl->windowfiles, i, cur) {
- if (git_mwindow_scan_recently_used(
- cur, &lru_window, &lru_last, false, GIT_MWINDOW__LRU)) {
- list = &cur->windows;
- }
- }
-
- if (!lru_window) {
- git_error_set(GIT_ERROR_OS, "failed to close memory window; couldn't find LRU");
- return -1;
- }
-
- ctl->mapped -= lru_window->window_map.len;
- git_futils_mmap_free(&lru_window->window_map);
-
- if (lru_last)
- lru_last->next = lru_window->next;
- else
- *list = lru_window->next;
-
- git__free(lru_window);
- ctl->open_windows--;
-
- return 0;
-}
-
-/*
- * Finds the file that does not have any open windows AND whose
- * most-recently-used window is the least-recently used one across all
- * currently open files.
- *
- * Called under lock from new_window_locked.
- */
-static int git_mwindow_find_lru_file_locked(git_mwindow_file **out)
-{
- git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
- git_mwindow_file *lru_file = NULL, *current_file = NULL;
- git_mwindow *lru_window = NULL;
- size_t i;
-
- git_vector_foreach(&ctl->windowfiles, i, current_file) {
- git_mwindow *mru_window = NULL;
- if (!git_mwindow_scan_recently_used(
- current_file, &mru_window, NULL, true, GIT_MWINDOW__MRU)) {
- continue;
- }
- if (!lru_window || lru_window->last_used > mru_window->last_used) {
- lru_window = mru_window;
- lru_file = current_file;
- }
- }
-
- if (!lru_file) {
- git_error_set(GIT_ERROR_OS, "failed to close memory window file; couldn't find LRU");
- return -1;
- }
-
- *out = lru_file;
- return 0;
-}
-
-/* This gets called under lock from git_mwindow_open */
-static git_mwindow *new_window_locked(
- git_file fd,
- off64_t size,
- off64_t offset)
-{
- git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
- size_t walign = git_mwindow__window_size / 2;
- off64_t len;
- git_mwindow *w;
-
- w = git__calloc(1, sizeof(*w));
-
- if (w == NULL)
- return NULL;
-
- w->offset = (offset / walign) * walign;
-
- len = size - w->offset;
- if (len > (off64_t)git_mwindow__window_size)
- len = (off64_t)git_mwindow__window_size;
-
- ctl->mapped += (size_t)len;
-
- while (git_mwindow__mapped_limit < ctl->mapped &&
- git_mwindow_close_lru_window_locked() == 0) /* nop */;
-
- /*
- * We treat `mapped_limit` as a soft limit. If we can't find a
- * window to close and are above the limit, we still mmap the new
- * window.
- */
-
- if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
- /*
- * The first error might be down to memory fragmentation even if
- * we're below our soft limits, so free up what we can and try again.
- */
-
- while (git_mwindow_close_lru_window_locked() == 0)
- /* nop */;
-
- if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
- git__free(w);
- return NULL;
- }
- }
-
- ctl->mmap_calls++;
- ctl->open_windows++;
-
- if (ctl->mapped > ctl->peak_mapped)
- ctl->peak_mapped = ctl->mapped;
-
- if (ctl->open_windows > ctl->peak_open_windows)
- ctl->peak_open_windows = ctl->open_windows;
-
- return w;
-}
-
-/*
- * Open a new window, closing the least recenty used until we have
- * enough space. Don't forget to add it to your list
- */
-unsigned char *git_mwindow_open(
- git_mwindow_file *mwf,
- git_mwindow **cursor,
- off64_t offset,
- size_t extra,
- unsigned int *left)
-{
- git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
- git_mwindow *w = *cursor;
-
- if (git_mutex_lock(&git__mwindow_mutex)) {
- git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex");
- return NULL;
- }
-
- if (!w || !(git_mwindow_contains(w, offset) && git_mwindow_contains(w, offset + extra))) {
- if (w) {
- w->inuse_cnt--;
- }
-
- for (w = mwf->windows; w; w = w->next) {
- if (git_mwindow_contains(w, offset) &&
- git_mwindow_contains(w, offset + extra))
- break;
- }
-
- /*
- * If there isn't a suitable window, we need to create a new
- * one.
- */
- if (!w) {
- w = new_window_locked(mwf->fd, mwf->size, offset);
- if (w == NULL) {
- git_mutex_unlock(&git__mwindow_mutex);
- return NULL;
- }
- w->next = mwf->windows;
- mwf->windows = w;
- }
- }
-
- /* If we changed w, store it in the cursor */
- if (w != *cursor) {
- w->last_used = ctl->used_ctr++;
- w->inuse_cnt++;
- *cursor = w;
- }
-
- offset -= w->offset;
-
- if (left)
- *left = (unsigned int)(w->window_map.len - offset);
-
- git_mutex_unlock(&git__mwindow_mutex);
- return (unsigned char *) w->window_map.data + offset;
-}
-
-int git_mwindow_file_register(git_mwindow_file *mwf)
-{
- git_vector closed_files = GIT_VECTOR_INIT;
- git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
- int error;
- size_t i;
- git_mwindow_file *closed_file = NULL;
-
- if (git_mutex_lock(&git__mwindow_mutex)) {
- git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex");
- return -1;
- }
-
- if (ctl->windowfiles.length == 0 &&
- (error = git_vector_init(&ctl->windowfiles, 8, NULL)) < 0) {
- git_mutex_unlock(&git__mwindow_mutex);
- goto cleanup;
- }
-
- if (git_mwindow__file_limit) {
- git_mwindow_file *lru_file;
- while (git_mwindow__file_limit <= ctl->windowfiles.length &&
- git_mwindow_find_lru_file_locked(&lru_file) == 0) {
- if ((error = git_vector_insert(&closed_files, lru_file)) < 0) {
- /*
- * Exceeding the file limit seems preferrable to being open to
- * data races that can end up corrupting the heap.
- */
- break;
- }
- git_mwindow_free_all_locked(lru_file);
- }
- }
-
- error = git_vector_insert(&ctl->windowfiles, mwf);
- git_mutex_unlock(&git__mwindow_mutex);
- if (error < 0)
- goto cleanup;
-
- /*
- * Once we have released the global windowfiles lock, we can close each
- * individual file. Before doing so, acquire that file's lock to avoid
- * closing a file that is currently being used.
- */
- git_vector_foreach(&closed_files, i, closed_file) {
- error = git_mutex_lock(&closed_file->lock);
- if (error < 0)
- continue;
- p_close(closed_file->fd);
- closed_file->fd = -1;
- git_mutex_unlock(&closed_file->lock);
- }
-
-cleanup:
- git_vector_free(&closed_files);
- return error;
-}
-
-void git_mwindow_file_deregister(git_mwindow_file *mwf)
-{
- git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
- git_mwindow_file *cur;
- size_t i;
-
- if (git_mutex_lock(&git__mwindow_mutex))
- return;
-
- git_vector_foreach(&ctl->windowfiles, i, cur) {
- if (cur == mwf) {
- git_vector_remove(&ctl->windowfiles, i);
- git_mutex_unlock(&git__mwindow_mutex);
- return;
- }
- }
- git_mutex_unlock(&git__mwindow_mutex);
-}
-
-void git_mwindow_close(git_mwindow **window)
-{
- git_mwindow *w = *window;
- if (w) {
- if (git_mutex_lock(&git__mwindow_mutex)) {
- git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex");
- return;
- }
-
- w->inuse_cnt--;
- git_mutex_unlock(&git__mwindow_mutex);
- *window = NULL;
- }
-}