diff options
Diffstat (limited to 'src/mwindow.c')
| -rw-r--r-- | src/mwindow.c | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/src/mwindow.c b/src/mwindow.c new file mode 100644 index 000000000..3ac585720 --- /dev/null +++ b/src/mwindow.c @@ -0,0 +1,271 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "common.h" +#include "mwindow.h" +#include "vector.h" +#include "fileops.h" +#include "map.h" + +#define DEFAULT_WINDOW_SIZE \ + (sizeof(void*) >= 8 \ + ? 1 * 1024 * 1024 * 1024 \ + : 32 * 1024 * 1024) + +#define DEFAULT_MAPPED_LIMIT \ + ((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256)) + +/* + * We need this because each process is only allowed a specific amount + * of memory. Making it writable should generate one instance per + * process, but we still need to set a couple of variables. + */ + +static git_mwindow_ctl ctl = { + .window_size = DEFAULT_WINDOW_SIZE, + .mapped_limit = DEFAULT_MAPPED_LIMIT +}; + +/* + * Free all the windows in a sequence, typically because we're done + * with the file + */ +void git_mwindow_free_all(git_mwindow_file *mwf) +{ + unsigned int 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; + 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; + free(w); + } +} + +/* + * Check if a window 'win' contains the address 'offset' + */ +int git_mwindow_contains(git_mwindow *win, off_t offset) +{ + off_t win_off = win->offset; + return win_off <= offset + && offset <= (off_t)(win_off + win->window_map.len); +} + +/* + * Find the least-recently-used window in a file + */ +void git_mwindow_scan_lru( + git_mwindow_file *mwf, + git_mwindow **lru_w, + git_mwindow **lru_l) +{ + git_mwindow *w, *w_l; + + for (w_l = NULL, w = mwf->windows; w; w = w->next) { + if (!w->inuse_cnt) { + /* + * If the current one is more recent than the last one, + * store it in the output parameter. If lru_w is NULL, + * it's the first loop, so store it as well. + */ + if (!*lru_w || w->last_used < (*lru_w)->last_used) { + *lru_w = w; + *lru_l = w_l; + } + } + w_l = w; + } +} + +/* + * Close the least recently used window. You should check to see if + * the file descriptors need closing from time to time. + */ +int git_mwindow_close_lru(git_mwindow_file *mwf) +{ + unsigned int i; + git_mwindow *lru_w = NULL, *lru_l = NULL; + + /* FIMXE: Does this give us any advantage? */ + if(mwf->windows) + git_mwindow_scan_lru(mwf, &lru_w, &lru_l); + + for (i = 0; i < ctl.windowfiles.length; ++i) { + git_mwindow_scan_lru(git_vector_get(&ctl.windowfiles, i), &lru_w, &lru_l); + } + + if (lru_w) { + git_mwindow_close(&lru_w); + ctl.mapped -= lru_w->window_map.len; + git_futils_mmap_free(&lru_w->window_map); + + if (lru_l) + lru_l->next = lru_w->next; + else + mwf->windows = lru_w->next; + + free(lru_w); + ctl.open_windows--; + + return GIT_SUCCESS; + } + + return git__throw(GIT_ERROR, "Failed to close memory window. Couln't find LRU"); +} + +static git_mwindow *new_window(git_mwindow_file *mwf, git_file fd, size_t size, off_t offset) +{ + size_t walign = ctl.window_size / 2; + size_t len; + git_mwindow *w; + + w = git__malloc(sizeof(*w)); + if (w == NULL) + return w; + + memset(w, 0x0, sizeof(*w)); + w->offset = (offset / walign) * walign; + + len = size - w->offset; + if (len > ctl.window_size) + len = ctl.window_size; + + ctl.mapped += len; + + while(ctl.mapped_limit < ctl.mapped && + git_mwindow_close_lru(mwf) == GIT_SUCCESS) {} + + /* FIXME: Shouldn't we error out if there's an error in closing lru? */ + + if (git_futils_mmap_ro(&w->window_map, fd, w->offset, len) < GIT_SUCCESS) + goto cleanup; + + 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; + +cleanup: + free(w); + return NULL; +} + +/* + * 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, git_file fd, + size_t size, off_t offset, int extra, unsigned int *left) +{ + git_mwindow *w = *cursor; + + if (!w || !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 + extra)) + break; + } + + /* + * If there isn't a suitable window, we need to create a new + * one. + */ + if (!w) { + w = new_window(mwf, fd, size, offset); + if (w == NULL) + 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; + assert(git__is_sizet(offset)); + + if (left) + *left = w->window_map.len - offset; + + return (unsigned char *) w->window_map.data + offset; + + free(w); + return NULL; +} + +int git_mwindow_file_register(git_mwindow_file *mwf) +{ + int error; + + if (ctl.windowfiles.length == 0 && + (error = git_vector_init(&ctl.windowfiles, 8, NULL)) < GIT_SUCCESS) + return error; + + return git_vector_insert(&ctl.windowfiles, mwf); +} + +void git_mwindow_close(git_mwindow **window) +{ + git_mwindow *w = *window; + if (w) { + w->inuse_cnt--; + *window = NULL; + } +} |
