Skip to content

Commit eab2b04

Browse files
committed
Review feedback
* Change the default of the file limit to 0 (unlimited). * Changed the heuristic to close files to be the file that contains the least-recently-used window such that the window is the most-recently-used in the file, and the file does not have in-use windows. * Parameterized the filelimit test to check for a limit of 1 and 100 open windows.
1 parent 9679df5 commit eab2b04

File tree

3 files changed

+133
-58
lines changed

3 files changed

+133
-58
lines changed

include/git2/common.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ typedef enum {
240240
* * opts(GIT_OPT_SET_MWINDOW_FILE_LIMIT, size_t):
241241
*
242242
* > Set the maximum number of files that can be mapped at any time
243-
* > by the library
243+
* > by the library. The default (0) is unlimited.
244244
*
245245
* * opts(GIT_OPT_GET_SEARCH_PATH, int level, git_buf *buf)
246246
*

src/mwindow.c

Lines changed: 95 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@
2222
#define DEFAULT_MAPPED_LIMIT \
2323
((1024 * 1024) * (sizeof(void*) >= 8 ? 8192ULL : 256UL))
2424

25-
/* default ulimit -n on macOS is just 256 */
26-
#define DEFAULT_FILE_LIMIT 128
25+
/* default is unlimited */
26+
#define DEFAULT_FILE_LIMIT 0
2727

2828
size_t git_mwindow__window_size = DEFAULT_WINDOW_SIZE;
2929
size_t git_mwindow__mapped_limit = DEFAULT_MAPPED_LIMIT;
3030
size_t git_mwindow__file_limit = DEFAULT_FILE_LIMIT;
3131

3232
/* Whenever you want to read or modify this, grab git__mwindow_mutex */
33-
static git_mwindow_ctl mem_ctl;
33+
git_mwindow_ctl git_mwindow__mem_ctl;
3434

3535
/* Global list of mwindow files, to open packs once across repos */
3636
git_strmap *git__pack_cache = NULL;
@@ -136,7 +136,7 @@ void git_mwindow_free_all(git_mwindow_file *mwf)
136136
*/
137137
void git_mwindow_free_all_locked(git_mwindow_file *mwf)
138138
{
139-
git_mwindow_ctl *ctl = &mem_ctl;
139+
git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
140140
size_t i;
141141

142142
/*
@@ -178,94 +178,131 @@ int git_mwindow_contains(git_mwindow *win, off64_t offset)
178178
&& offset <= (off64_t)(win_off + win->window_map.len);
179179
}
180180

181+
#define GIT_MWINDOW__LRU -1
182+
#define GIT_MWINDOW__MRU 1
183+
181184
/*
182-
* Find the least-recently-used window in a file
185+
* Find the least- or most-recently-used window in a file that is not currently
186+
* being used. The 'only_unused' flag controls whether the caller requires the
187+
* file to only have unused windows.
188+
*
189+
* Returns whether such a window was found in the file.
183190
*/
184-
static void git_mwindow_scan_lru(
185-
git_mwindow_file *mwf,
186-
git_mwindow **lru_w,
187-
git_mwindow **lru_l)
191+
static bool git_mwindow_scan_recently_used(
192+
git_mwindow_file *mwf,
193+
git_mwindow **out_window,
194+
git_mwindow **out_last,
195+
bool only_unused,
196+
int comparison_sign)
188197
{
189-
git_mwindow *w, *w_l;
190-
191-
for (w_l = NULL, w = mwf->windows; w; w = w->next) {
192-
if (!w->inuse_cnt) {
193-
/*
194-
* If the current one is more recent than the last one,
195-
* store it in the output parameter. If lru_w is NULL,
196-
* it's the first loop, so store it as well.
197-
*/
198-
if (!*lru_w || w->last_used < (*lru_w)->last_used) {
199-
*lru_w = w;
200-
*lru_l = w_l;
201-
}
198+
git_mwindow *w, *w_last;
199+
git_mwindow *lru_window = NULL, *lru_last = NULL;
200+
201+
assert(mwf);
202+
assert(out_window);
203+
204+
lru_window = *out_window;
205+
if (out_last)
206+
lru_last = *out_last;
207+
208+
for (w_last = NULL, w = mwf->windows; w; w_last = w, w = w->next) {
209+
if (w->inuse_cnt) {
210+
if (only_unused)
211+
return false;
212+
/* This window is currently being used. Skip it. */
213+
continue;
214+
}
215+
216+
/*
217+
* If the current one is more (or less) recent than the last one,
218+
* store it in the output parameter. If lru_window is NULL,
219+
* it's the first loop, so store it as well.
220+
*/
221+
if (!lru_window ||
222+
(comparison_sign == GIT_MWINDOW__LRU && lru_window->last_used > w->last_used) ||
223+
(comparison_sign == GIT_MWINDOW__MRU && lru_window->last_used < w->last_used)) {
224+
lru_window = w;
225+
lru_last = w_last;
202226
}
203-
w_l = w;
204227
}
228+
229+
if (!lru_window && !lru_last)
230+
return false;
231+
232+
*out_window = lru_window;
233+
if (out_last)
234+
*out_last = lru_last;
235+
return true;
205236
}
206237

207238
/*
208-
* Close the least recently used window. Called under lock from new_window.
239+
* Close the least recently used window (that is currently not being used) out
240+
* of all the files. Called under lock from new_window.
209241
*/
210242
static int git_mwindow_close_lru_window(void)
211243
{
212-
git_mwindow_ctl *ctl = &mem_ctl;
244+
git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
213245
git_mwindow_file *cur;
214246
size_t i;
215-
git_mwindow *lru_w = NULL, *lru_l = NULL, **list = NULL;
247+
git_mwindow *lru_window = NULL, *lru_last = NULL, **list = NULL;
216248

217249
git_vector_foreach(&ctl->windowfiles, i, cur) {
218-
git_mwindow *last = lru_w;
219-
git_mwindow_scan_lru(cur, &lru_w, &lru_l);
220-
if (lru_w != last)
250+
if (git_mwindow_scan_recently_used(
251+
cur, &lru_window, &lru_last, false, GIT_MWINDOW__LRU)) {
221252
list = &cur->windows;
253+
}
222254
}
223255

224-
if (!lru_w) {
256+
if (!lru_window) {
225257
git_error_set(GIT_ERROR_OS, "failed to close memory window; couldn't find LRU");
226258
return -1;
227259
}
228260

229-
ctl->mapped -= lru_w->window_map.len;
230-
git_futils_mmap_free(&lru_w->window_map);
261+
ctl->mapped -= lru_window->window_map.len;
262+
git_futils_mmap_free(&lru_window->window_map);
231263

232-
if (lru_l)
233-
lru_l->next = lru_w->next;
264+
if (lru_last)
265+
lru_last->next = lru_window->next;
234266
else
235-
*list = lru_w->next;
267+
*list = lru_window->next;
236268

237-
git__free(lru_w);
269+
git__free(lru_window);
238270
ctl->open_windows--;
239271

240272
return 0;
241273
}
242274

243275
/*
244-
* Close the file that contains the least recently used window. Called under
245-
* lock from new_window.
276+
* Close the file that does not have any open windows AND contains the
277+
* least-recently-used most-recently-used window.
278+
*
279+
* Called under lock from new_window.
246280
*/
247281
static int git_mwindow_close_lru_file(void)
248282
{
249-
git_mwindow_ctl *ctl = &mem_ctl;
250-
git_mwindow_file *lru_f = NULL, *cur;
283+
git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
284+
git_mwindow_file *lru_file = NULL, *current_file = NULL;
285+
git_mwindow *lru_window = NULL;
251286
size_t i;
252-
git_mwindow *lru_w = NULL, *lru_l = NULL;
253287

254-
git_vector_foreach(&ctl->windowfiles, i, cur) {
255-
git_mwindow *last = lru_w;
256-
git_mwindow_scan_lru(cur, &lru_w, &lru_l);
257-
if (lru_w != last)
258-
lru_f = cur;
288+
git_vector_foreach(&ctl->windowfiles, i, current_file) {
289+
git_mwindow *mru_window = NULL;
290+
if (!git_mwindow_scan_recently_used(
291+
current_file, &mru_window, NULL, true, GIT_MWINDOW__MRU)) {
292+
continue;
293+
}
294+
if (!lru_window || lru_window->last_used > mru_window->last_used)
295+
lru_file = current_file;
259296
}
260297

261-
if (!lru_f) {
298+
if (!lru_file) {
262299
git_error_set(GIT_ERROR_OS, "failed to close memory window file; couldn't find LRU");
263300
return -1;
264301
}
265302

266-
git_mwindow_free_all_locked(lru_f);
267-
p_close(lru_f->fd);
268-
lru_f->fd = -1;
303+
git_mwindow_free_all_locked(lru_file);
304+
p_close(lru_file->fd);
305+
lru_file->fd = -1;
269306

270307
return 0;
271308
}
@@ -276,7 +313,7 @@ static git_mwindow *new_window(
276313
off64_t size,
277314
off64_t offset)
278315
{
279-
git_mwindow_ctl *ctl = &mem_ctl;
316+
git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
280317
size_t walign = git_mwindow__window_size / 2;
281318
off64_t len;
282319
git_mwindow *w;
@@ -342,7 +379,7 @@ unsigned char *git_mwindow_open(
342379
size_t extra,
343380
unsigned int *left)
344381
{
345-
git_mwindow_ctl *ctl = &mem_ctl;
382+
git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
346383
git_mwindow *w = *cursor;
347384

348385
if (git_mutex_lock(&git__mwindow_mutex)) {
@@ -394,7 +431,7 @@ unsigned char *git_mwindow_open(
394431

395432
int git_mwindow_file_register(git_mwindow_file *mwf)
396433
{
397-
git_mwindow_ctl *ctl = &mem_ctl;
434+
git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
398435
int ret;
399436

400437
if (git_mutex_lock(&git__mwindow_mutex)) {
@@ -408,8 +445,10 @@ int git_mwindow_file_register(git_mwindow_file *mwf)
408445
return -1;
409446
}
410447

411-
while (git_mwindow__file_limit <= ctl->windowfiles.length &&
412-
git_mwindow_close_lru_file() == 0) /* nop */;
448+
if (git_mwindow__file_limit) {
449+
while (git_mwindow__file_limit <= ctl->windowfiles.length &&
450+
git_mwindow_close_lru_file() == 0) /* nop */;
451+
}
413452

414453
ret = git_vector_insert(&ctl->windowfiles, mwf);
415454
git_mutex_unlock(&git__mwindow_mutex);
@@ -419,7 +458,7 @@ int git_mwindow_file_register(git_mwindow_file *mwf)
419458

420459
void git_mwindow_file_deregister(git_mwindow_file *mwf)
421460
{
422-
git_mwindow_ctl *ctl = &mem_ctl;
461+
git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
423462
git_mwindow_file *cur;
424463
size_t i;
425464

tests/pack/filelimit.c

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,39 @@
11
#include "clar_libgit2.h"
2+
#include "mwindow.h"
3+
#include "global.h"
24
#include <git2.h>
35

6+
extern git_mwindow_ctl git_mwindow__mem_ctl;
7+
size_t mwindow_file_limit = 0;
8+
size_t original_mwindow_file_limit = 0;
9+
10+
void test_pack_filelimit__initialize_tiny(void)
11+
{
12+
mwindow_file_limit = 1;
13+
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_MWINDOW_FILE_LIMIT, &original_mwindow_file_limit));
14+
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_MWINDOW_FILE_LIMIT, mwindow_file_limit));
15+
}
16+
17+
void test_pack_filelimit__initialize_medium(void)
18+
{
19+
mwindow_file_limit = 100;
20+
cl_git_pass(git_libgit2_opts(GIT_OPT_GET_MWINDOW_FILE_LIMIT, &original_mwindow_file_limit));
21+
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_MWINDOW_FILE_LIMIT, mwindow_file_limit));
22+
}
23+
24+
void test_pack_filelimit__cleanup(void)
25+
{
26+
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_MWINDOW_FILE_LIMIT, original_mwindow_file_limit));
27+
}
28+
429
void test_pack_filelimit__open_repo_with_1025_packfiles(void)
530
{
31+
git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
632
git_repository *repo;
733
git_revwalk *walk;
834
git_oid id;
935
int i;
36+
unsigned int open_windows;
1037

1138
/*
1239
* This repository contains 1025 packfiles, each with one commit, one tree,
@@ -30,7 +57,16 @@ void test_pack_filelimit__open_repo_with_1025_packfiles(void)
3057
++i;
3158
cl_assert_equal_i(1025, i);
3259

60+
cl_git_pass(git_mutex_lock(&git__mwindow_mutex));
61+
/*
62+
* Adding an assert while holding a lock will cause the whole process to
63+
* deadlock. Copy the value and do the assert after releasing the lock.
64+
*/
65+
open_windows = ctl->open_windows;
66+
cl_git_pass(git_mutex_unlock(&git__mwindow_mutex));
67+
68+
cl_assert_equal_i(mwindow_file_limit, open_windows);
69+
3370
git_revwalk_free(walk);
3471
git_repository_free(repo);
3572
}
36-

0 commit comments

Comments
 (0)