Line Flow Count Block(s) Source
1 - /*
2 - * Copyright (C) the libgit2 contributors. All rights reserved.
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 "mwindow.h"
9 -
10 - #include "vector.h"
11 - #include "futils.h"
12 - #include "map.h"
13 - #include "global.h"
14 - #include "strmap.h"
15 - #include "pack.h"
16 -
17 - #define DEFAULT_WINDOW_SIZE \
18 - (sizeof(void*) >= 8 \
19 - ? 1 * 1024 * 1024 * 1024 \
20 - : 32 * 1024 * 1024)
21 -
22 - #define DEFAULT_MAPPED_LIMIT \
23 - ((1024 * 1024) * (sizeof(void*) >= 8 ? 8192ULL : 256UL))
24 -
25 - size_t git_mwindow__window_size = DEFAULT_WINDOW_SIZE;
26 - size_t git_mwindow__mapped_limit = DEFAULT_MAPPED_LIMIT;
27 -
28 - /* Whenever you want to read or modify this, grab git__mwindow_mutex */
29 - static git_mwindow_ctl mem_ctl;
30 -
31 - /* Global list of mwindow files, to open packs once across repos */
32 - git_strmap *git__pack_cache = NULL;
33 -
34 9 2 static void git_mwindow_files_free(void)
35 - {
36 9 2 git_strmap *tmp = git__pack_cache;
37 -
38 9 2 git__pack_cache = NULL;
39 9 2 git_strmap_free(tmp);
40 9 3 }
41 -
42 9 2 int git_mwindow_global_init(void)
43 - {
44 9 2,3 assert(!git__pack_cache);
45 -
46 9 4 git__on_shutdown(git_mwindow_files_free);
47 9 5 return git_strmap_new(&git__pack_cache);
48 - }
49 -
50 6499 2 int git_mwindow_get_pack(struct git_pack_file **out, const char *path)
51 - {
52 - struct git_pack_file *pack;
53 - char *packname;
54 - int error;
55 -
56 6528 2,3 if ((error = git_packfile__name(&packname, path)) < 0)
57 ##### 4 return error;
58 -
59 6580 5,6 if (git_mutex_lock(&git__mwindow_mutex) < 0) {
60 ##### 7 git_error_set(GIT_ERROR_OS, "failed to lock mwindow mutex");
61 ##### 8 return -1;
62 - }
63 -
64 6580 9 pack = git_strmap_get(git__pack_cache, packname);
65 6580 10 git__free(packname);
66 -
67 6580 11 if (pack != NULL) {
68 4078 12 git_atomic_inc(&pack->refcount);
69 4078 13 git_mutex_unlock(&git__mwindow_mutex);
70 4067 14 *out = pack;
71 4067 14 return 0;
72 - }
73 -
74 - /* If we didn't find it, we need to create it */
75 2502 15,16 if ((error = git_packfile_alloc(&pack, path)) < 0) {
76 ##### 17 git_mutex_unlock(&git__mwindow_mutex);
77 ##### 18 return error;
78 - }
79 -
80 2502 19 git_atomic_inc(&pack->refcount);
81 -
82 2502 20 error = git_strmap_set(git__pack_cache, pack->pack_name, pack);
83 2502 21 git_mutex_unlock(&git__mwindow_mutex);
84 -
85 2502 22 if (error < 0) {
86 ##### 23 git_packfile_free(pack);
87 ##### 24 return -1;
88 - }
89 -
90 2502 25 *out = pack;
91 2502 25 return 0;
92 - }
93 -
94 6527 2 void git_mwindow_put_pack(struct git_pack_file *pack)
95 - {
96 - int count;
97 -
98 6580 2,3 if (git_mutex_lock(&git__mwindow_mutex) < 0)
99 ##### 4 return;
100 -
101 - /* put before get would be a corrupted state */
102 6580 5,6 assert(git__pack_cache);
103 -
104 - /* if we cannot find it, the state is corrupted */
105 6580 7-9 assert(git_strmap_exists(git__pack_cache, pack->pack_name));
106 -
107 6580 10 count = git_atomic_dec(&pack->refcount);
108 6580 11 if (count == 0) {
109 2502 12 git_strmap_delete(git__pack_cache, pack->pack_name);
110 2502 13 git_packfile_free(pack);
111 - }
112 -
113 6580 14 git_mutex_unlock(&git__mwindow_mutex);
114 6568 15 return;
115 - }
116 -
117 4854 2 void git_mwindow_free_all(git_mwindow_file *mwf)
118 - {
119 4854 2,3 if (git_mutex_lock(&git__mwindow_mutex)) {
120 ##### 4 git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex");
121 4854 5,8 return;
122 - }
123 -
124 4854 6 git_mwindow_free_all_locked(mwf);
125 -
126 4854 7 git_mutex_unlock(&git__mwindow_mutex);
127 - }
128 -
129 - /*
130 - * Free all the windows in a sequence, typically because we're done
131 - * with the file
132 - */
133 5299 2 void git_mwindow_free_all_locked(git_mwindow_file *mwf)
134 - {
135 5299 2 git_mwindow_ctl *ctl = &mem_ctl;
136 - size_t i;
137 -
138 - /*
139 - * Remove these windows from the global list
140 - */
141 13606 2,7,8 for (i = 0; i < ctl->windowfiles.length; ++i){
142 8842 3,4 if (git_vector_get(&ctl->windowfiles, i) == mwf) {
143 535 5 git_vector_remove(&ctl->windowfiles, i);
144 535 6 break;
145 - }
146 - }
147 -
148 5299 9 if (ctl->windowfiles.length == 0) {
149 1010 10 git_vector_free(&ctl->windowfiles);
150 1010 11 ctl->windowfiles.contents = NULL;
151 - }
152 -
153 7975 12,17 while (mwf->windows) {
154 2676 13 git_mwindow *w = mwf->windows;
155 2676 13,14 assert(w->inuse_cnt == 0);
156 -
157 2676 15 ctl->mapped -= w->window_map.len;
158 2676 15 ctl->open_windows--;
159 -
160 2676 15 git_futils_mmap_free(&w->window_map);
161 -
162 2676 16 mwf->windows = w->next;
163 2676 16 git__free(w);
164 - }
165 5299 18 }
166 -
167 - /*
168 - * Check if a window 'win' contains the address 'offset'
169 - */
170 201558 2 int git_mwindow_contains(git_mwindow *win, off64_t offset)
171 - {
172 201558 2 off64_t win_off = win->offset;
173 201558 6 return win_off <= offset
174 201558 2-5 && offset <= (off64_t)(win_off + win->window_map.len);
175 - }
176 -
177 - /*
178 - * Find the least-recently-used window in a file
179 - */
180 ##### 2 static void git_mwindow_scan_lru(
181 - git_mwindow_file *mwf,
182 - git_mwindow **lru_w,
183 - git_mwindow **lru_l)
184 - {
185 - git_mwindow *w, *w_l;
186 -
187 ##### 2,7,8 for (w_l = NULL, w = mwf->windows; w; w = w->next) {
188 ##### 3 if (!w->inuse_cnt) {
189 - /*
190 - * If the current one is more recent than the last one,
191 - * store it in the output parameter. If lru_w is NULL,
192 - * it's the first loop, so store it as well.
193 - */
194 ##### 4,5 if (!*lru_w || w->last_used < (*lru_w)->last_used) {
195 ##### 6 *lru_w = w;
196 ##### 6 *lru_l = w_l;
197 - }
198 - }
199 ##### 7 w_l = w;
200 - }
201 ##### 9 }
202 -
203 - /*
204 - * Close the least recently used window. You should check to see if
205 - * the file descriptors need closing from time to time. Called under
206 - * lock from new_window.
207 - */
208 ##### 2 static int git_mwindow_close_lru(git_mwindow_file *mwf)
209 - {
210 ##### 2 git_mwindow_ctl *ctl = &mem_ctl;
211 - size_t i;
212 ##### 2 git_mwindow *lru_w = NULL, *lru_l = NULL, **list = &mwf->windows;
213 -
214 - /* FIXME: Does this give us any advantage? */
215 ##### 2 if(mwf->windows)
216 ##### 3 git_mwindow_scan_lru(mwf, &lru_w, &lru_l);
217 -
218 ##### 4,9,10 for (i = 0; i < ctl->windowfiles.length; ++i) {
219 ##### 5 git_mwindow *last = lru_w;
220 ##### 5 git_mwindow_file *cur = git_vector_get(&ctl->windowfiles, i);
221 ##### 6 git_mwindow_scan_lru(cur, &lru_w, &lru_l);
222 ##### 7 if (lru_w != last)
223 ##### 8 list = &cur->windows;
224 - }
225 -
226 ##### 11 if (!lru_w) {
227 ##### 12 git_error_set(GIT_ERROR_OS, "failed to close memory window; couldn't find LRU");
228 ##### 13 return -1;
229 - }
230 -
231 ##### 14 ctl->mapped -= lru_w->window_map.len;
232 ##### 14 git_futils_mmap_free(&lru_w->window_map);
233 -
234 ##### 15 if (lru_l)
235 ##### 16 lru_l->next = lru_w->next;
236 - else
237 ##### 17 *list = lru_w->next;
238 -
239 ##### 18 git__free(lru_w);
240 ##### 19 ctl->open_windows--;
241 -
242 ##### 19 return 0;
243 - }
244 -
245 - /* This gets called under lock from git_mwindow_open */
246 2676 2 static git_mwindow *new_window(
247 - git_mwindow_file *mwf,
248 - git_file fd,
249 - off64_t size,
250 - off64_t offset)
251 - {
252 2676 2 git_mwindow_ctl *ctl = &mem_ctl;
253 2676 2 size_t walign = git_mwindow__window_size / 2;
254 - off64_t len;
255 - git_mwindow *w;
256 -
257 2676 2 w = git__malloc(sizeof(*w));
258 -
259 2676 3 if (w == NULL)
260 ##### 4 return NULL;
261 -
262 2676 5 memset(w, 0x0, sizeof(*w));
263 2676 5 w->offset = (offset / walign) * walign;
264 -
265 2676 5 len = size - w->offset;
266 2676 5 if (len > (off64_t)git_mwindow__window_size)
267 ##### 6 len = (off64_t)git_mwindow__window_size;
268 -
269 2676 7 ctl->mapped += (size_t)len;
270 -
271 2676 7,8,10 while (git_mwindow__mapped_limit < ctl->mapped &&
272 ##### 9 git_mwindow_close_lru(mwf) == 0) /* nop */;
273 -
274 - /*
275 - * We treat `mapped_limit` as a soft limit. If we can't find a
276 - * window to close and are above the limit, we still mmap the new
277 - * window.
278 - */
279 -
280 2676 11,12 if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
281 - /*
282 - * The first error might be down to memory fragmentation even if
283 - * we're below our soft limits, so free up what we can and try again.
284 - */
285 -
286 ##### 13-15 while (git_mwindow_close_lru(mwf) == 0)
287 - /* nop */;
288 -
289 ##### 16,17 if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
290 ##### 18 git__free(w);
291 ##### 19 return NULL;
292 - }
293 - }
294 -
295 2676 20 ctl->mmap_calls++;
296 2676 20 ctl->open_windows++;
297 -
298 2676 20 if (ctl->mapped > ctl->peak_mapped)
299 13 21 ctl->peak_mapped = ctl->mapped;
300 -
301 2676 22 if (ctl->open_windows > ctl->peak_open_windows)
302 6 23 ctl->peak_open_windows = ctl->open_windows;
303 -
304 2676 24 return w;
305 - }
306 -
307 - /*
308 - * Open a new window, closing the least recenty used until we have
309 - * enough space. Don't forget to add it to your list
310 - */
311 103448 2 unsigned char *git_mwindow_open(
312 - git_mwindow_file *mwf,
313 - git_mwindow **cursor,
314 - off64_t offset,
315 - size_t extra,
316 - unsigned int *left)
317 - {
318 103448 2 git_mwindow_ctl *ctl = &mem_ctl;
319 103448 2 git_mwindow *w = *cursor;
320 -
321 103455 2,3 if (git_mutex_lock(&git__mwindow_mutex)) {
322 ##### 4 git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex");
323 ##### 5 return NULL;
324 - }
325 -
326 103455 6-10 if (!w || !(git_mwindow_contains(w, offset) && git_mwindow_contains(w, offset + extra))) {
327 103455 11 if (w) {
328 ##### 12 w->inuse_cnt--;
329 - }
330 -
331 103455 13,19,20 for (w = mwf->windows; w; w = w->next) {
332 100779 14,15,17 if (git_mwindow_contains(w, offset) &&
333 100779 16 git_mwindow_contains(w, offset + extra))
334 100779 18 break;
335 - }
336 -
337 - /*
338 - * If there isn't a suitable window, we need to create a new
339 - * one.
340 - */
341 103455 21 if (!w) {
342 2676 22 w = new_window(mwf, mwf->fd, mwf->size, offset);
343 2676 23 if (w == NULL) {
344 ##### 24 git_mutex_unlock(&git__mwindow_mutex);
345 ##### 25 return NULL;
346 - }
347 2676 26 w->next = mwf->windows;
348 2676 26 mwf->windows = w;
349 - }
350 - }
351 -
352 - /* If we changed w, store it in the cursor */
353 103455 27 if (w != *cursor) {
354 103455 28 w->last_used = ctl->used_ctr++;
355 103455 28 w->inuse_cnt++;
356 103455 28 *cursor = w;
357 - }
358 -
359 103455 29 offset -= w->offset;
360 -
361 103455 29 if (left)
362 103455 30 *left = (unsigned int)(w->window_map.len - offset);
363 -
364 103455 31 git_mutex_unlock(&git__mwindow_mutex);
365 103453 32 return (unsigned char *) w->window_map.data + offset;
366 - }
367 -
368 535 2 int git_mwindow_file_register(git_mwindow_file *mwf)
369 - {
370 535 2 git_mwindow_ctl *ctl = &mem_ctl;
371 - int ret;
372 -
373 535 2,3 if (git_mutex_lock(&git__mwindow_mutex)) {
374 ##### 4 git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex");
375 ##### 5 return -1;
376 - }
377 -
378 535 6,8 if (ctl->windowfiles.length == 0 &&
379 392 7 git_vector_init(&ctl->windowfiles, 8, NULL) < 0) {
380 ##### 9 git_mutex_unlock(&git__mwindow_mutex);
381 ##### 10 return -1;
382 - }
383 -
384 535 11 ret = git_vector_insert(&ctl->windowfiles, mwf);
385 535 12 git_mutex_unlock(&git__mwindow_mutex);
386 -
387 535 13 return ret;
388 - }
389 -
390 ##### 2 void git_mwindow_file_deregister(git_mwindow_file *mwf)
391 - {
392 ##### 2 git_mwindow_ctl *ctl = &mem_ctl;
393 - git_mwindow_file *cur;
394 - size_t i;
395 -
396 ##### 2,3 if (git_mutex_lock(&git__mwindow_mutex))
397 ##### 4 return;
398 -
399 ##### 5,10-12 git_vector_foreach(&ctl->windowfiles, i, cur) {
400 ##### 6 if (cur == mwf) {
401 ##### 7 git_vector_remove(&ctl->windowfiles, i);
402 ##### 8 git_mutex_unlock(&git__mwindow_mutex);
403 ##### 9 return;
404 - }
405 - }
406 ##### 13 git_mutex_unlock(&git__mwindow_mutex);
407 - }
408 -
409 143415 2 void git_mwindow_close(git_mwindow **window)
410 - {
411 143415 2 git_mwindow *w = *window;
412 143415 2 if (w) {
413 103455 3,4 if (git_mutex_lock(&git__mwindow_mutex)) {
414 ##### 5 git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex");
415 143422 6,9 return;
416 - }
417 -
418 103455 7 w->inuse_cnt--;
419 103455 7 git_mutex_unlock(&git__mwindow_mutex);
420 103455 8 *window = NULL;
421 - }
422 - }