2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
17 #define DEFAULT_WINDOW_SIZE \
19 ? 1 * 1024 * 1024 * 1024 \
22 #define DEFAULT_MAPPED_LIMIT \
23 ((1024 * 1024) * (sizeof(void*) >= 8 ? 8192ULL : 256UL))
25 size_t git_mwindow__window_size
= DEFAULT_WINDOW_SIZE
;
26 size_t git_mwindow__mapped_limit
= DEFAULT_MAPPED_LIMIT
;
28 /* Whenever you want to read or modify this, grab git__mwindow_mutex */
29 static git_mwindow_ctl mem_ctl
;
31 /* Global list of mwindow files, to open packs once across repos */
32 git_strmap
*git__pack_cache
= NULL
;
34 static void git_mwindow_files_free(void)
36 git_strmap
*tmp
= git__pack_cache
;
38 git__pack_cache
= NULL
;
42 int git_mwindow_global_init(void)
44 assert(!git__pack_cache
);
46 git__on_shutdown(git_mwindow_files_free
);
47 return git_strmap_alloc(&git__pack_cache
);
50 int git_mwindow_get_pack(struct git_pack_file
**out
, const char *path
)
55 struct git_pack_file
*pack
;
57 if ((error
= git_packfile__name(&packname
, path
)) < 0)
60 if (git_mutex_lock(&git__mwindow_mutex
) < 0) {
61 git_error_set(GIT_ERROR_OS
, "failed to lock mwindow mutex");
65 pos
= git_strmap_lookup_index(git__pack_cache
, packname
);
68 if (git_strmap_valid_index(git__pack_cache
, pos
)) {
69 pack
= git_strmap_value_at(git__pack_cache
, pos
);
70 git_atomic_inc(&pack
->refcount
);
72 git_mutex_unlock(&git__mwindow_mutex
);
77 /* If we didn't find it, we need to create it */
78 if ((error
= git_packfile_alloc(&pack
, path
)) < 0) {
79 git_mutex_unlock(&git__mwindow_mutex
);
83 git_atomic_inc(&pack
->refcount
);
85 git_strmap_insert(git__pack_cache
, pack
->pack_name
, pack
, &error
);
86 git_mutex_unlock(&git__mwindow_mutex
);
89 git_packfile_free(pack
);
97 void git_mwindow_put_pack(struct git_pack_file
*pack
)
102 if (git_mutex_lock(&git__mwindow_mutex
) < 0)
105 /* put before get would be a corrupted state */
106 assert(git__pack_cache
);
108 pos
= git_strmap_lookup_index(git__pack_cache
, pack
->pack_name
);
109 /* if we cannot find it, the state is corrupted */
110 assert(git_strmap_valid_index(git__pack_cache
, pos
));
112 count
= git_atomic_dec(&pack
->refcount
);
114 git_strmap_delete_at(git__pack_cache
, pos
);
115 git_packfile_free(pack
);
118 git_mutex_unlock(&git__mwindow_mutex
);
122 void git_mwindow_free_all(git_mwindow_file
*mwf
)
124 if (git_mutex_lock(&git__mwindow_mutex
)) {
125 git_error_set(GIT_ERROR_THREAD
, "unable to lock mwindow mutex");
129 git_mwindow_free_all_locked(mwf
);
131 git_mutex_unlock(&git__mwindow_mutex
);
135 * Free all the windows in a sequence, typically because we're done
138 void git_mwindow_free_all_locked(git_mwindow_file
*mwf
)
140 git_mwindow_ctl
*ctl
= &mem_ctl
;
144 * Remove these windows from the global list
146 for (i
= 0; i
< ctl
->windowfiles
.length
; ++i
){
147 if (git_vector_get(&ctl
->windowfiles
, i
) == mwf
) {
148 git_vector_remove(&ctl
->windowfiles
, i
);
153 if (ctl
->windowfiles
.length
== 0) {
154 git_vector_free(&ctl
->windowfiles
);
155 ctl
->windowfiles
.contents
= NULL
;
158 while (mwf
->windows
) {
159 git_mwindow
*w
= mwf
->windows
;
160 assert(w
->inuse_cnt
== 0);
162 ctl
->mapped
-= w
->window_map
.len
;
165 git_futils_mmap_free(&w
->window_map
);
167 mwf
->windows
= w
->next
;
173 * Check if a window 'win' contains the address 'offset'
175 int git_mwindow_contains(git_mwindow
*win
, git_off_t offset
)
177 git_off_t win_off
= win
->offset
;
178 return win_off
<= offset
179 && offset
<= (git_off_t
)(win_off
+ win
->window_map
.len
);
183 * Find the least-recently-used window in a file
185 static void git_mwindow_scan_lru(
186 git_mwindow_file
*mwf
,
190 git_mwindow
*w
, *w_l
;
192 for (w_l
= NULL
, w
= mwf
->windows
; w
; w
= w
->next
) {
195 * If the current one is more recent than the last one,
196 * store it in the output parameter. If lru_w is NULL,
197 * it's the first loop, so store it as well.
199 if (!*lru_w
|| w
->last_used
< (*lru_w
)->last_used
) {
209 * Close the least recently used window. You should check to see if
210 * the file descriptors need closing from time to time. Called under
211 * lock from new_window.
213 static int git_mwindow_close_lru(git_mwindow_file
*mwf
)
215 git_mwindow_ctl
*ctl
= &mem_ctl
;
217 git_mwindow
*lru_w
= NULL
, *lru_l
= NULL
, **list
= &mwf
->windows
;
219 /* FIXME: Does this give us any advantage? */
221 git_mwindow_scan_lru(mwf
, &lru_w
, &lru_l
);
223 for (i
= 0; i
< ctl
->windowfiles
.length
; ++i
) {
224 git_mwindow
*last
= lru_w
;
225 git_mwindow_file
*cur
= git_vector_get(&ctl
->windowfiles
, i
);
226 git_mwindow_scan_lru(cur
, &lru_w
, &lru_l
);
228 list
= &cur
->windows
;
232 git_error_set(GIT_ERROR_OS
, "failed to close memory window; couldn't find LRU");
236 ctl
->mapped
-= lru_w
->window_map
.len
;
237 git_futils_mmap_free(&lru_w
->window_map
);
240 lru_l
->next
= lru_w
->next
;
250 /* This gets called under lock from git_mwindow_open */
251 static git_mwindow
*new_window(
252 git_mwindow_file
*mwf
,
257 git_mwindow_ctl
*ctl
= &mem_ctl
;
258 size_t walign
= git_mwindow__window_size
/ 2;
262 w
= git__malloc(sizeof(*w
));
267 memset(w
, 0x0, sizeof(*w
));
268 w
->offset
= (offset
/ walign
) * walign
;
270 len
= size
- w
->offset
;
271 if (len
> (git_off_t
)git_mwindow__window_size
)
272 len
= (git_off_t
)git_mwindow__window_size
;
274 ctl
->mapped
+= (size_t)len
;
276 while (git_mwindow__mapped_limit
< ctl
->mapped
&&
277 git_mwindow_close_lru(mwf
) == 0) /* nop */;
280 * We treat `mapped_limit` as a soft limit. If we can't find a
281 * window to close and are above the limit, we still mmap the new
285 if (git_futils_mmap_ro(&w
->window_map
, fd
, w
->offset
, (size_t)len
) < 0) {
287 * The first error might be down to memory fragmentation even if
288 * we're below our soft limits, so free up what we can and try again.
291 while (git_mwindow_close_lru(mwf
) == 0)
294 if (git_futils_mmap_ro(&w
->window_map
, fd
, w
->offset
, (size_t)len
) < 0) {
303 if (ctl
->mapped
> ctl
->peak_mapped
)
304 ctl
->peak_mapped
= ctl
->mapped
;
306 if (ctl
->open_windows
> ctl
->peak_open_windows
)
307 ctl
->peak_open_windows
= ctl
->open_windows
;
313 * Open a new window, closing the least recenty used until we have
314 * enough space. Don't forget to add it to your list
316 unsigned char *git_mwindow_open(
317 git_mwindow_file
*mwf
,
318 git_mwindow
**cursor
,
323 git_mwindow_ctl
*ctl
= &mem_ctl
;
324 git_mwindow
*w
= *cursor
;
326 if (git_mutex_lock(&git__mwindow_mutex
)) {
327 git_error_set(GIT_ERROR_THREAD
, "unable to lock mwindow mutex");
331 if (!w
|| !(git_mwindow_contains(w
, offset
) && git_mwindow_contains(w
, offset
+ extra
))) {
336 for (w
= mwf
->windows
; w
; w
= w
->next
) {
337 if (git_mwindow_contains(w
, offset
) &&
338 git_mwindow_contains(w
, offset
+ extra
))
343 * If there isn't a suitable window, we need to create a new
347 w
= new_window(mwf
, mwf
->fd
, mwf
->size
, offset
);
349 git_mutex_unlock(&git__mwindow_mutex
);
352 w
->next
= mwf
->windows
;
357 /* If we changed w, store it in the cursor */
359 w
->last_used
= ctl
->used_ctr
++;
367 *left
= (unsigned int)(w
->window_map
.len
- offset
);
369 git_mutex_unlock(&git__mwindow_mutex
);
370 return (unsigned char *) w
->window_map
.data
+ offset
;
373 int git_mwindow_file_register(git_mwindow_file
*mwf
)
375 git_mwindow_ctl
*ctl
= &mem_ctl
;
378 if (git_mutex_lock(&git__mwindow_mutex
)) {
379 git_error_set(GIT_ERROR_THREAD
, "unable to lock mwindow mutex");
383 if (ctl
->windowfiles
.length
== 0 &&
384 git_vector_init(&ctl
->windowfiles
, 8, NULL
) < 0) {
385 git_mutex_unlock(&git__mwindow_mutex
);
389 ret
= git_vector_insert(&ctl
->windowfiles
, mwf
);
390 git_mutex_unlock(&git__mwindow_mutex
);
395 void git_mwindow_file_deregister(git_mwindow_file
*mwf
)
397 git_mwindow_ctl
*ctl
= &mem_ctl
;
398 git_mwindow_file
*cur
;
401 if (git_mutex_lock(&git__mwindow_mutex
))
404 git_vector_foreach(&ctl
->windowfiles
, i
, cur
) {
406 git_vector_remove(&ctl
->windowfiles
, i
);
407 git_mutex_unlock(&git__mwindow_mutex
);
411 git_mutex_unlock(&git__mwindow_mutex
);
414 void git_mwindow_close(git_mwindow
**window
)
416 git_mwindow
*w
= *window
;
418 if (git_mutex_lock(&git__mwindow_mutex
)) {
419 git_error_set(GIT_ERROR_THREAD
, "unable to lock mwindow mutex");
424 git_mutex_unlock(&git__mwindow_mutex
);