Skip to content

Commit c793430

Browse files
spearcegitster
authored andcommitted
Limit file descriptors used by packs
Rather than using 'errno == EMFILE' after a failed open() call to indicate the process is out of file descriptors and an LRU pack window should be closed, place a hard upper limit on the number of open packs based on the actual rlimit of the process. By using a hard upper limit that is below the rlimit of the current process it is not necessary to check for EMFILE on every single fd-allocating system call. Instead reserving 25 file descriptors makes it safe to assume the system call won't fail due to being over the filedescriptor limit. Here 25 is chosen as a WAG, but considers 3 for stdin/stdout/stderr, and at least a few for other Git code to operate on temporary files. An additional 20 is reserved as it is not known what the C library needs to perform other services on Git's behalf, such as nsswitch or name resolution. This fixes a case where running `git gc --auto` in a repository with more than 1024 packs (but an rlimit of 1024 open fds) fails due to the temporary output file not being able to allocate a file descriptor. The output file is opened by pack-objects after object enumeration and delta compression are done, both of which have already opened all of the packs and fully populated the file descriptor table. Signed-off-by: Shawn O. Pearce <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 62270f6 commit c793430

File tree

1 file changed

+30
-13
lines changed

1 file changed

+30
-13
lines changed

sha1_file.c

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,8 @@ static unsigned int pack_used_ctr;
417417
static unsigned int pack_mmap_calls;
418418
static unsigned int peak_pack_open_windows;
419419
static unsigned int pack_open_windows;
420+
static unsigned int pack_open_fds;
421+
static unsigned int pack_max_fds;
420422
static size_t peak_pack_mapped;
421423
static size_t pack_mapped;
422424
struct packed_git *packed_git;
@@ -596,6 +598,7 @@ static int unuse_one_window(struct packed_git *current, int keep_fd)
596598
lru_p->windows = lru_w->next;
597599
if (!lru_p->windows && lru_p->pack_fd != keep_fd) {
598600
close(lru_p->pack_fd);
601+
pack_open_fds--;
599602
lru_p->pack_fd = -1;
600603
}
601604
}
@@ -680,8 +683,10 @@ void free_pack_by_name(const char *pack_name)
680683
if (strcmp(pack_name, p->pack_name) == 0) {
681684
clear_delta_base_cache();
682685
close_pack_windows(p);
683-
if (p->pack_fd != -1)
686+
if (p->pack_fd != -1) {
684687
close(p->pack_fd);
688+
pack_open_fds--;
689+
}
685690
close_pack_index(p);
686691
free(p->bad_object_sha1);
687692
*pp = p->next;
@@ -707,9 +712,29 @@ static int open_packed_git_1(struct packed_git *p)
707712
if (!p->index_data && open_pack_index(p))
708713
return error("packfile %s index unavailable", p->pack_name);
709714

715+
if (!pack_max_fds) {
716+
struct rlimit lim;
717+
unsigned int max_fds;
718+
719+
if (getrlimit(RLIMIT_NOFILE, &lim))
720+
die_errno("cannot get RLIMIT_NOFILE");
721+
722+
max_fds = lim.rlim_cur;
723+
724+
/* Save 3 for stdin/stdout/stderr, 22 for work */
725+
if (25 < max_fds)
726+
pack_max_fds = max_fds - 25;
727+
else
728+
pack_max_fds = 1;
729+
}
730+
731+
while (pack_max_fds <= pack_open_fds && unuse_one_window(NULL, -1))
732+
; /* nothing */
733+
710734
p->pack_fd = git_open_noatime(p->pack_name, p);
711735
if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
712736
return -1;
737+
pack_open_fds++;
713738

714739
/* If we created the struct before we had the pack we lack size. */
715740
if (!p->pack_size) {
@@ -761,6 +786,7 @@ static int open_packed_git(struct packed_git *p)
761786
return 0;
762787
if (p->pack_fd != -1) {
763788
close(p->pack_fd);
789+
pack_open_fds--;
764790
p->pack_fd = -1;
765791
}
766792
return -1;
@@ -918,6 +944,9 @@ struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
918944

919945
void install_packed_git(struct packed_git *pack)
920946
{
947+
if (pack->pack_fd != -1)
948+
pack_open_fds++;
949+
921950
pack->next = packed_git;
922951
packed_git = pack;
923952
}
@@ -935,8 +964,6 @@ static void prepare_packed_git_one(char *objdir, int local)
935964
sprintf(path, "%s/pack", objdir);
936965
len = strlen(path);
937966
dir = opendir(path);
938-
while (!dir && errno == EMFILE && unuse_one_window(NULL, -1))
939-
dir = opendir(path);
940967
if (!dir) {
941968
if (errno != ENOENT)
942969
error("unable to open object pack directory: %s: %s",
@@ -1092,14 +1119,6 @@ static int git_open_noatime(const char *name, struct packed_git *p)
10921119
if (fd >= 0)
10931120
return fd;
10941121

1095-
/* Might the failure be insufficient file descriptors? */
1096-
if (errno == EMFILE) {
1097-
if (unuse_one_window(p, -1))
1098-
continue;
1099-
else
1100-
return -1;
1101-
}
1102-
11031122
/* Might the failure be due to O_NOATIME? */
11041123
if (errno != ENOENT && sha1_file_open_flag) {
11051124
sha1_file_open_flag = 0;
@@ -2359,8 +2378,6 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
23592378

23602379
filename = sha1_file_name(sha1);
23612380
fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
2362-
while (fd < 0 && errno == EMFILE && unuse_one_window(NULL, -1))
2363-
fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
23642381
if (fd < 0) {
23652382
if (errno == EACCES)
23662383
return error("insufficient permission for adding an object to repository database %s\n", get_object_directory());

0 commit comments

Comments
 (0)