Release pack windows before reporting out of memory.
[gitweb.git] / sha1_file.c
index 63123cc47be29430565f594340374271248f01bc..fb1032b0fdf9f778441c49664da4f5f6b5da57f5 100644 (file)
@@ -398,10 +398,34 @@ static char *find_sha1_file(const unsigned char *sha1, struct stat *st)
 }
 
 static unsigned int pack_used_ctr;
+static unsigned int pack_mmap_calls;
+static unsigned int peak_pack_open_windows;
+static unsigned int pack_open_windows;
+static size_t peak_pack_mapped;
 static size_t pack_mapped;
 static size_t page_size;
 struct packed_git *packed_git;
 
+void pack_report()
+{
+       fprintf(stderr,
+               "pack_report: getpagesize()            = %10lu\n"
+               "pack_report: core.packedGitWindowSize = %10lu\n"
+               "pack_report: core.packedGitLimit      = %10lu\n",
+               page_size,
+               packed_git_window_size,
+               packed_git_limit);
+       fprintf(stderr,
+               "pack_report: pack_used_ctr            = %10u\n"
+               "pack_report: pack_mmap_calls          = %10u\n"
+               "pack_report: pack_open_windows        = %10u / %10u\n"
+               "pack_report: pack_mapped              = %10lu / %10lu\n",
+               pack_used_ctr,
+               pack_mmap_calls,
+               pack_open_windows, peak_pack_open_windows,
+               pack_mapped, peak_pack_mapped);
+}
+
 static int check_packed_git_idx(const char *path, unsigned long *idx_size_,
                                void **idx_map_)
 {
@@ -451,23 +475,34 @@ static int check_packed_git_idx(const char *path, unsigned long *idx_size_,
        return 0;
 }
 
-static int unuse_one_window(void)
+static void scan_windows(struct packed_git *p,
+       struct packed_git **lru_p,
+       struct pack_window **lru_w,
+       struct pack_window **lru_l)
 {
-       struct packed_git *p, *lru_p = NULL;
-       struct pack_window *w, *w_l, *lru_w = NULL, *lru_l = NULL;
+       struct pack_window *w, *w_l;
 
-       for (p = packed_git; p; p = p->next) {
-               for (w_l = NULL, w = p->windows; w; w = w->next) {
-                       if (!w->inuse_cnt) {
-                               if (!lru_w || w->last_used < lru_w->last_used) {
-                                       lru_p = p;
-                                       lru_w = w;
-                                       lru_l = w_l;
-                               }
+       for (w_l = NULL, w = p->windows; w; w = w->next) {
+               if (!w->inuse_cnt) {
+                       if (!*lru_w || w->last_used < (*lru_w)->last_used) {
+                               *lru_p = p;
+                               *lru_w = w;
+                               *lru_l = w_l;
                        }
-                       w_l = w;
                }
+               w_l = w;
        }
+}
+
+static int unuse_one_window(struct packed_git *current)
+{
+       struct packed_git *p, *lru_p = NULL;
+       struct pack_window *lru_w = NULL, *lru_l = NULL;
+
+       if (current)
+               scan_windows(current, &lru_p, &lru_w, &lru_l);
+       for (p = packed_git; p; p = p->next)
+               scan_windows(p, &lru_p, &lru_w, &lru_l);
        if (lru_p) {
                munmap(lru_w->base, lru_w->len);
                pack_mapped -= lru_w->len;
@@ -475,17 +510,25 @@ static int unuse_one_window(void)
                        lru_l->next = lru_w->next;
                else {
                        lru_p->windows = lru_w->next;
-                       if (!lru_p->windows) {
+                       if (!lru_p->windows && lru_p != current) {
                                close(lru_p->pack_fd);
                                lru_p->pack_fd = -1;
                        }
                }
                free(lru_w);
+               pack_open_windows--;
                return 1;
        }
        return 0;
 }
 
+void release_pack_memory(size_t need)
+{
+       size_t cur = pack_mapped;
+       while (need >= (cur - pack_mapped) && unuse_one_window(NULL))
+               ; /* nothing */
+}
+
 void unuse_pack(struct pack_window **w_cursor)
 {
        struct pack_window *w = *w_cursor;
@@ -584,7 +627,8 @@ unsigned char* use_pack(struct packed_git *p,
                        if (win->len > packed_git_window_size)
                                win->len = packed_git_window_size;
                        pack_mapped += win->len;
-                       while (packed_git_limit < pack_mapped && unuse_one_window())
+                       while (packed_git_limit < pack_mapped
+                               && unuse_one_window(p))
                                ; /* nothing */
                        win->base = mmap(NULL, win->len,
                                PROT_READ, MAP_PRIVATE,
@@ -593,6 +637,12 @@ unsigned char* use_pack(struct packed_git *p,
                                die("packfile %s cannot be mapped: %s",
                                        p->pack_name,
                                        strerror(errno));
+                       pack_mmap_calls++;
+                       pack_open_windows++;
+                       if (pack_mapped > peak_pack_mapped)
+                               peak_pack_mapped = pack_mapped;
+                       if (pack_open_windows > peak_pack_open_windows)
+                               peak_pack_open_windows = pack_open_windows;
                        win->next = p->windows;
                        p->windows = win;
                }