]> git.proxmox.com Git - libgit2.git/blob - src/mwindow.c
Cleanup legal data
[libgit2.git] / src / mwindow.c
1 /*
2 * Copyright (C) 2009-2011 the libgit2 contributors
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
8 #include "common.h"
9 #include "mwindow.h"
10 #include "vector.h"
11 #include "fileops.h"
12 #include "map.h"
13
14 #define DEFAULT_WINDOW_SIZE \
15 (sizeof(void*) >= 8 \
16 ? 1 * 1024 * 1024 * 1024 \
17 : 32 * 1024 * 1024)
18
19 #define DEFAULT_MAPPED_LIMIT \
20 ((1024 * 1024) * (sizeof(void*) >= 8 ? 8192ULL : 256UL))
21
22 /*
23 * We need this because each process is only allowed a specific amount
24 * of memory. Making it writable should generate one instance per
25 * process, but we still need to set a couple of variables.
26 */
27
28 static git_mwindow_ctl ctl = {
29 0,
30 0,
31 DEFAULT_WINDOW_SIZE,
32 DEFAULT_MAPPED_LIMIT,
33 0,
34 0,
35 0,
36 0,
37 {0, 0, 0, 0, 0}
38 };
39
40 /*
41 * Free all the windows in a sequence, typically because we're done
42 * with the file
43 */
44 void git_mwindow_free_all(git_mwindow_file *mwf)
45 {
46 unsigned int i;
47 /*
48 * Remove these windows from the global list
49 */
50 for (i = 0; i < ctl.windowfiles.length; ++i){
51 if (git_vector_get(&ctl.windowfiles, i) == mwf) {
52 git_vector_remove(&ctl.windowfiles, i);
53 break;
54 }
55 }
56
57 if (ctl.windowfiles.length == 0) {
58 git_vector_free(&ctl.windowfiles);
59 ctl.windowfiles.contents = NULL;
60 }
61
62 while (mwf->windows) {
63 git_mwindow *w = mwf->windows;
64 assert(w->inuse_cnt == 0);
65
66 ctl.mapped -= w->window_map.len;
67 ctl.open_windows--;
68
69 git_futils_mmap_free(&w->window_map);
70
71 mwf->windows = w->next;
72 free(w);
73 }
74 }
75
76 /*
77 * Check if a window 'win' contains the address 'offset'
78 */
79 int git_mwindow_contains(git_mwindow *win, git_off_t offset)
80 {
81 git_off_t win_off = win->offset;
82 return win_off <= offset
83 && offset <= (git_off_t)(win_off + win->window_map.len);
84 }
85
86 /*
87 * Find the least-recently-used window in a file
88 */
89 void git_mwindow_scan_lru(
90 git_mwindow_file *mwf,
91 git_mwindow **lru_w,
92 git_mwindow **lru_l)
93 {
94 git_mwindow *w, *w_l;
95
96 for (w_l = NULL, w = mwf->windows; w; w = w->next) {
97 if (!w->inuse_cnt) {
98 /*
99 * If the current one is more recent than the last one,
100 * store it in the output parameter. If lru_w is NULL,
101 * it's the first loop, so store it as well.
102 */
103 if (!*lru_w || w->last_used < (*lru_w)->last_used) {
104 *lru_w = w;
105 *lru_l = w_l;
106 }
107 }
108 w_l = w;
109 }
110 }
111
112 /*
113 * Close the least recently used window. You should check to see if
114 * the file descriptors need closing from time to time.
115 */
116 int git_mwindow_close_lru(git_mwindow_file *mwf)
117 {
118 unsigned int i;
119 git_mwindow *lru_w = NULL, *lru_l = NULL;
120
121 /* FIMXE: Does this give us any advantage? */
122 if(mwf->windows)
123 git_mwindow_scan_lru(mwf, &lru_w, &lru_l);
124
125 for (i = 0; i < ctl.windowfiles.length; ++i) {
126 git_mwindow_scan_lru(git_vector_get(&ctl.windowfiles, i), &lru_w, &lru_l);
127 }
128
129 if (lru_w) {
130 git_mwindow_close(&lru_w);
131 ctl.mapped -= lru_w->window_map.len;
132 git_futils_mmap_free(&lru_w->window_map);
133
134 if (lru_l)
135 lru_l->next = lru_w->next;
136 else
137 mwf->windows = lru_w->next;
138
139 free(lru_w);
140 ctl.open_windows--;
141
142 return GIT_SUCCESS;
143 }
144
145 return git__throw(GIT_ERROR, "Failed to close memory window. Couln't find LRU");
146 }
147
148 static git_mwindow *new_window(git_mwindow_file *mwf, git_file fd, git_off_t size, git_off_t offset)
149 {
150 size_t walign = ctl.window_size / 2;
151 git_off_t len;
152 git_mwindow *w;
153
154 w = git__malloc(sizeof(*w));
155 if (w == NULL)
156 return w;
157
158 memset(w, 0x0, sizeof(*w));
159 w->offset = (offset / walign) * walign;
160
161 len = size - w->offset;
162 if (len > (git_off_t)ctl.window_size)
163 len = (git_off_t)ctl.window_size;
164
165 ctl.mapped += (size_t)len;
166
167 while(ctl.mapped_limit < ctl.mapped &&
168 git_mwindow_close_lru(mwf) == GIT_SUCCESS) {}
169
170 /* FIXME: Shouldn't we error out if there's an error in closing lru? */
171
172 if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < GIT_SUCCESS)
173 goto cleanup;
174
175 ctl.mmap_calls++;
176 ctl.open_windows++;
177
178 if (ctl.mapped > ctl.peak_mapped)
179 ctl.peak_mapped = ctl.mapped;
180
181 if (ctl.open_windows > ctl.peak_open_windows)
182 ctl.peak_open_windows = ctl.open_windows;
183
184 return w;
185
186 cleanup:
187 free(w);
188 return NULL;
189 }
190
191 /*
192 * Open a new window, closing the least recenty used until we have
193 * enough space. Don't forget to add it to your list
194 */
195 unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor,
196 git_off_t offset, int extra, unsigned int *left)
197 {
198 git_mwindow *w = *cursor;
199
200 if (!w || !git_mwindow_contains(w, offset + extra)) {
201 if (w) {
202 w->inuse_cnt--;
203 }
204
205 for (w = mwf->windows; w; w = w->next) {
206 if (git_mwindow_contains(w, offset + extra))
207 break;
208 }
209
210 /*
211 * If there isn't a suitable window, we need to create a new
212 * one.
213 */
214 if (!w) {
215 w = new_window(mwf, mwf->fd, mwf->size, offset);
216 if (w == NULL)
217 return NULL;
218 w->next = mwf->windows;
219 mwf->windows = w;
220 }
221 }
222
223 /* If we changed w, store it in the cursor */
224 if (w != *cursor) {
225 w->last_used = ctl.used_ctr++;
226 w->inuse_cnt++;
227 *cursor = w;
228 }
229
230 offset -= w->offset;
231 assert(git__is_sizet(offset));
232
233 if (left)
234 *left = (unsigned int)(w->window_map.len - offset);
235
236 return (unsigned char *) w->window_map.data + offset;
237 }
238
239 int git_mwindow_file_register(git_mwindow_file *mwf)
240 {
241 int error;
242
243 if (ctl.windowfiles.length == 0 &&
244 (error = git_vector_init(&ctl.windowfiles, 8, NULL)) < GIT_SUCCESS)
245 return error;
246
247 return git_vector_insert(&ctl.windowfiles, mwf);
248 }
249
250 void git_mwindow_close(git_mwindow **window)
251 {
252 git_mwindow *w = *window;
253 if (w) {
254 w->inuse_cnt--;
255 *window = NULL;
256 }
257 }