]> git.proxmox.com Git - libgit2.git/blob - src/mwindow.c
Oh yeah, bugs from my rebase
[libgit2.git] / src / mwindow.c
1 /*
2 * Copyright (C) 2009-2012 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 #include "global.h"
14
15 #define DEFAULT_WINDOW_SIZE \
16 (sizeof(void*) >= 8 \
17 ? 1 * 1024 * 1024 * 1024 \
18 : 32 * 1024 * 1024)
19
20 #define DEFAULT_MAPPED_LIMIT \
21 ((1024 * 1024) * (sizeof(void*) >= 8 ? 8192ULL : 256UL))
22
23 /*
24 * These are the global options for mmmap limits.
25 * TODO: allow the user to change these
26 */
27 static struct {
28 size_t window_size;
29 size_t mapped_limit;
30 } _mw_options = {
31 DEFAULT_WINDOW_SIZE,
32 DEFAULT_MAPPED_LIMIT,
33 };
34
35 /* Whenever you want to read or modify this, grab git__mwindow_mutex */
36 static git_mwindow_ctl mem_ctl;
37
38 /*
39 * Free all the windows in a sequence, typically because we're done
40 * with the file
41 */
42 void git_mwindow_free_all(git_mwindow_file *mwf)
43 {
44 git_mwindow_ctl *ctl = &mem_ctl;
45 unsigned int i;
46
47 if (git_mutex_lock(&git__mwindow_mutex)) {
48 giterr_set(GITERR_THREAD, "unable to lock mwindow mutex");
49 return;
50 }
51
52 /*
53 * Remove these windows from the global list
54 */
55 for (i = 0; i < ctl->windowfiles.length; ++i){
56 if (git_vector_get(&ctl->windowfiles, i) == mwf) {
57 git_vector_remove(&ctl->windowfiles, i);
58 break;
59 }
60 }
61
62 if (ctl->windowfiles.length == 0) {
63 git_vector_free(&ctl->windowfiles);
64 ctl->windowfiles.contents = NULL;
65 }
66
67 while (mwf->windows) {
68 git_mwindow *w = mwf->windows;
69 assert(w->inuse_cnt == 0);
70
71 ctl->mapped -= w->window_map.len;
72 ctl->open_windows--;
73
74 git_futils_mmap_free(&w->window_map);
75
76 mwf->windows = w->next;
77 git__free(w);
78 }
79
80 git_mutex_unlock(&git__mwindow_mutex);
81 }
82
83 /*
84 * Check if a window 'win' contains the address 'offset'
85 */
86 int git_mwindow_contains(git_mwindow *win, git_off_t offset)
87 {
88 git_off_t win_off = win->offset;
89 return win_off <= offset
90 && offset <= (git_off_t)(win_off + win->window_map.len);
91 }
92
93 /*
94 * Find the least-recently-used window in a file
95 */
96 static void git_mwindow_scan_lru(
97 git_mwindow_file *mwf,
98 git_mwindow **lru_w,
99 git_mwindow **lru_l)
100 {
101 git_mwindow *w, *w_l;
102
103 for (w_l = NULL, w = mwf->windows; w; w = w->next) {
104 if (!w->inuse_cnt) {
105 /*
106 * If the current one is more recent than the last one,
107 * store it in the output parameter. If lru_w is NULL,
108 * it's the first loop, so store it as well.
109 */
110 if (!*lru_w || w->last_used < (*lru_w)->last_used) {
111 *lru_w = w;
112 *lru_l = w_l;
113 }
114 }
115 w_l = w;
116 }
117 }
118
119 /*
120 * Close the least recently used window. You should check to see if
121 * the file descriptors need closing from time to time. Called under
122 * lock from new_window.
123 */
124 static int git_mwindow_close_lru(git_mwindow_file *mwf)
125 {
126 git_mwindow_ctl *ctl = &mem_ctl;
127 unsigned int i;
128 git_mwindow *lru_w = NULL, *lru_l = NULL, **list = &mwf->windows;
129
130 /* FIXME: Does this give us any advantage? */
131 if(mwf->windows)
132 git_mwindow_scan_lru(mwf, &lru_w, &lru_l);
133
134 for (i = 0; i < ctl->windowfiles.length; ++i) {
135 git_mwindow *last = lru_w;
136 git_mwindow_file *cur = git_vector_get(&ctl->windowfiles, i);
137 git_mwindow_scan_lru(cur, &lru_w, &lru_l);
138 if (lru_w != last)
139 list = &cur->windows;
140 }
141
142 if (!lru_w) {
143 giterr_set(GITERR_OS, "Failed to close memory window. Couldn't find LRU");
144 return -1;
145 }
146
147 ctl->mapped -= lru_w->window_map.len;
148 git_futils_mmap_free(&lru_w->window_map);
149
150 if (lru_l)
151 lru_l->next = lru_w->next;
152 else
153 *list = lru_w->next;
154
155 git__free(lru_w);
156 ctl->open_windows--;
157
158 return 0;
159 }
160
161 /* This gets called under lock from git_mwindow_open */
162 static git_mwindow *new_window(
163 git_mwindow_file *mwf,
164 git_file fd,
165 git_off_t size,
166 git_off_t offset)
167 {
168 git_mwindow_ctl *ctl = &mem_ctl;
169 size_t walign = _mw_options.window_size / 2;
170 git_off_t len;
171 git_mwindow *w;
172
173 w = git__malloc(sizeof(*w));
174
175 if (w == NULL)
176 return NULL;
177
178 memset(w, 0x0, sizeof(*w));
179 w->offset = (offset / walign) * walign;
180
181 len = size - w->offset;
182 if (len > (git_off_t)_mw_options.window_size)
183 len = (git_off_t)_mw_options.window_size;
184
185 ctl->mapped += (size_t)len;
186
187 while (_mw_options.mapped_limit < ctl->mapped &&
188 git_mwindow_close_lru(mwf) == 0) /* nop */;
189
190 /*
191 * We treat _mw_options.mapped_limit as a soft limit. If we can't find a
192 * window to close and are above the limit, we still mmap the new
193 * window.
194 */
195
196 if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
197 git__free(w);
198 return NULL;
199 }
200
201 ctl->mmap_calls++;
202 ctl->open_windows++;
203
204 if (ctl->mapped > ctl->peak_mapped)
205 ctl->peak_mapped = ctl->mapped;
206
207 if (ctl->open_windows > ctl->peak_open_windows)
208 ctl->peak_open_windows = ctl->open_windows;
209
210 return w;
211 }
212
213 /*
214 * Open a new window, closing the least recenty used until we have
215 * enough space. Don't forget to add it to your list
216 */
217 unsigned char *git_mwindow_open(
218 git_mwindow_file *mwf,
219 git_mwindow **cursor,
220 git_off_t offset,
221 size_t extra,
222 unsigned int *left)
223 {
224 git_mwindow_ctl *ctl = &mem_ctl;
225 git_mwindow *w = *cursor;
226
227 if (git_mutex_lock(&git__mwindow_mutex)) {
228 giterr_set(GITERR_THREAD, "unable to lock mwindow mutex");
229 return NULL;
230 }
231
232 if (!w || !(git_mwindow_contains(w, offset) && git_mwindow_contains(w, offset + extra))) {
233 if (w) {
234 w->inuse_cnt--;
235 }
236
237 for (w = mwf->windows; w; w = w->next) {
238 if (git_mwindow_contains(w, offset) &&
239 git_mwindow_contains(w, offset + extra))
240 break;
241 }
242
243 /*
244 * If there isn't a suitable window, we need to create a new
245 * one.
246 */
247 if (!w) {
248 w = new_window(mwf, mwf->fd, mwf->size, offset);
249 if (w == NULL) {
250 git_mutex_unlock(&git__mwindow_mutex);
251 return NULL;
252 }
253 w->next = mwf->windows;
254 mwf->windows = w;
255 }
256 }
257
258 /* If we changed w, store it in the cursor */
259 if (w != *cursor) {
260 w->last_used = ctl->used_ctr++;
261 w->inuse_cnt++;
262 *cursor = w;
263 }
264
265 offset -= w->offset;
266
267 if (left)
268 *left = (unsigned int)(w->window_map.len - offset);
269
270 git_mutex_unlock(&git__mwindow_mutex);
271 return (unsigned char *) w->window_map.data + offset;
272 }
273
274 int git_mwindow_file_register(git_mwindow_file *mwf)
275 {
276 git_mwindow_ctl *ctl = &mem_ctl;
277 int ret;
278
279 if (git_mutex_lock(&git__mwindow_mutex)) {
280 giterr_set(GITERR_THREAD, "unable to lock mwindow mutex");
281 return -1;
282 }
283
284 if (ctl->windowfiles.length == 0 &&
285 git_vector_init(&ctl->windowfiles, 8, NULL) < 0) {
286 git_mutex_unlock(&git__mwindow_mutex);
287 return -1;
288 }
289
290 ret = git_vector_insert(&ctl->windowfiles, mwf);
291 git_mutex_unlock(&git__mwindow_mutex);
292
293 return ret;
294 }
295
296 int git_mwindow_file_deregister(git_mwindow_file *mwf)
297 {
298 git_mwindow_ctl *ctl = &mem_ctl;
299 git_mwindow_file *cur;
300 unsigned int i;
301
302 if (git_mutex_lock(&git__mwindow_mutex)) {
303 giterr_set(GITERR_THREAD, "unable to lock mwindow mutex");
304 return -1;
305 }
306
307 git_vector_foreach(&ctl->windowfiles, i, cur) {
308 if (cur == mwf) {
309 git_vector_remove(&ctl->windowfiles, i);
310 git_mutex_unlock(&git__mwindow_mutex);
311 return 0;
312 }
313 }
314 git_mutex_unlock(&git__mwindow_mutex);
315
316 giterr_set(GITERR_ODB, "Failed to find the memory window file to deregister");
317 return -1;
318 }
319
320 void git_mwindow_close(git_mwindow **window)
321 {
322 git_mwindow *w = *window;
323 if (w) {
324 if (git_mutex_lock(&git__mwindow_mutex)) {
325 giterr_set(GITERR_THREAD, "unable to lock mwindow mutex");
326 return;
327 }
328
329 w->inuse_cnt--;
330 git_mutex_unlock(&git__mwindow_mutex);
331 *window = NULL;
332 }
333 }