{"thread":{"id":"5059","subject":"Fix double \"close()\" in ce_compare_data","startedAt":"2006-07-31T16:55:15Z","lastAt":"2006-08-08T23:44:09Z","messageCount":8,"participants":["Linus Torvalds","Junio C Hamano","Johannes Schindelin"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"24439","messageId":"Pine.LNX.4.64.0607310945490.4168@g5.osdl.org","threadId":"5059","inReplyTo":null,"subject":"Fix double \"close()\" in ce_compare_data","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-07-31T16:55:15Z","receivedAt":"2006-07-31T16:55:15Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nDoing an \"strace\" on \"git diff\" shows that we close() a file descriptor \ntwice (getting EBADFD on the second one) when we end up in ce_compare_data \nif the index does not match the checked-out stat information.\n\nThe \"index_fd()\" function will already have closed the fd for us, so we \nshould not close it again.\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\n---\n\nThe way I found this also showed a potential performance problem: if you \ndo a \"git reset --hard\" (or similar) after you have changes in your tree, \nit will write the index file with the same timestamp as the checked out \nfiles that it re-wrote.\n\nThat will also then forever afterwards (well, until the next \"git \nupdate-index --refresh\") cause the \"uncommon\" timestamp case in \nce_match_stat(), where we check the index-file timestamp against the \ntimestamp of the stat data, to trigger.\n\nNot very good. The \"ce_modified_check_fs()\" tests can be quite expensive \nif you have lots of those files because we end up then calling the \n\"ce_compare_data()\" function a lot. And suddenly \"git diff\" doesn't take a \ntenth of a second any more.\n\nWe should really try to have some way to re-generate the index \nautomatically when this case triggers, so that we only need to do it \n_once_ rather than keep doing it forever while the index is \"potentially \nstale\".\n\nAny ideas?\n\ndiff --git a/read-cache.c b/read-cache.c\nindex c0b0313..f92cdaa 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -61,7 +61,7 @@ static int ce_compare_data(struct cache_\n \t\tunsigned char sha1[20];\n \t\tif (!index_fd(sha1, fd, st, 0, NULL))\n \t\t\tmatch = memcmp(sha1, ce->sha1, 20);\n-\t\tclose(fd);\n+\t\t/* index_fd() closed the file descriptor already */\n \t}\n \treturn match;\n }\n"},{"id":"24454","messageId":"7vlkq961ea.fsf@assigned-by-dhcp.cox.net","threadId":"5059","inReplyTo":"Pine.LNX.4.64.0607310945490.4168@g5.osdl.org","subject":"Re: Fix double \"close()\" in ce_compare_data","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-07-31T19:05:17Z","receivedAt":"2006-07-31T19:05:17Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> The way I found this also showed a potential performance problem: if you \n> do a \"git reset --hard\" (or similar) after you have changes in your tree, \n> it will write the index file with the same timestamp as the checked out \n> files that it re-wrote.\n>...\n> We should really try to have some way to re-generate the index \n> automatically when this case triggers, so that we only need to do it \n> _once_ rather than keep doing it forever while the index is \"potentially \n> stale\".\n>\n> Any ideas?\n\nJust before writing the index out, we could do a gettimeofday()\nand wait for 1 second (or two if you are on FAT) if we have many\nmany paths that have the same timestamp.  Ugly and would not\nwork on NFS I suspect.\n\nWe could do gratuitous \"update-index --refresh\" but that is\nuglier especially if done at the core level; the caller does not\nexpect the index file to be updated.\n\nOr we could remove the check and tell the user not to touch the\nworking tree after calling update-index (I am not seriously\nsuggesting this).\n"},{"id":"24818","messageId":"7virl7e8ed.fsf@assigned-by-dhcp.cox.net","threadId":"5059","inReplyTo":"Pine.LNX.4.64.0607310945490.4168@g5.osdl.org","subject":"[PATCH] Racy git: avoid having to be always too careful","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-08-05T11:20:26Z","receivedAt":"2006-08-05T11:20:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Immediately after a bulk checkout, most of the paths in the\nworking tree would have the same timestamp as the index file,\nand this would force ce_match_stat() to take slow path for all\nof them.  When writing an index file out, if many of the paths\nhave very new (read: the same timestamp as the index file being\nwritten out) timestamp, we are better off delaying the return\nfrom the command, to make sure that later command to touch the\nworking tree files will leave newer timestamps than recorded in\nthe index, thereby avoiding to take the slow path.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\nLinus Torvalds <torvalds@osdl.org> writes:\n\n> Doing an \"strace\" on \"git diff\" shows that we close() a file descriptor \n> twice (getting EBADFD on the second one) when we end up in ce_compare_data \n> if the index does not match the checked-out stat information.\n>\n> The \"index_fd()\" function will already have closed the fd for us, so we \n> should not close it again.\n>\n> Signed-off-by: Linus Torvalds <torvalds@osdl.org>\n> ---\n>\n> The way I found this also showed a potential performance problem: if you \n> do a \"git reset --hard\" (or similar) after you have changes in your tree, \n> it will write the index file with the same timestamp as the checked out \n> files that it re-wrote.\n>\n> That will also then forever afterwards (well, until the next \"git \n> update-index --refresh\") cause the \"uncommon\" timestamp case in \n> ce_match_stat(), where we check the index-file timestamp against the \n> timestamp of the stat data, to trigger.\n>\n> Not very good. The \"ce_modified_check_fs()\" tests can be quite expensive \n> if you have lots of those files because we end up then calling the \n> \"ce_compare_data()\" function a lot. And suddenly \"git diff\" doesn't take a \n> tenth of a second any more.\n>\n> We should really try to have some way to re-generate the index \n> automatically when this case triggers, so that we only need to do it \n> _once_ rather than keep doing it forever while the index is \"potentially \n> stale\".\n>\n> Any ideas?\n\nHere is what I came up with.  I am not very happy with its\nmagic, but should be better than nothing.\n\n read-cache.c |   44 ++++++++++++++++++++++++++++++++++++++++----\n 1 files changed, 40 insertions(+), 4 deletions(-)\n\ndiff --git a/read-cache.c b/read-cache.c\nindex f92cdaa..ce76c20 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -5,6 +5,7 @@\n  */\n #include \"cache.h\"\n #include \"cache-tree.h\"\n+#include <time.h>\n \n /* Index extensions.\n  *\n@@ -923,7 +924,7 @@ static void ce_smudge_racily_clean_entry\n \t\t * $ echo filfre >nitfol\n \t\t * $ git-update-index --add nitfol\n \t\t *\n-\t\t * but it does not.  Whe the second update-index runs,\n+\t\t * but it does not.  When the second update-index runs,\n \t\t * it notices that the entry \"frotz\" has the same timestamp\n \t\t * as index, and if we were to smudge it by resetting its\n \t\t * size to zero here, then the object name recorded\n@@ -945,7 +946,9 @@ int write_cache(int newfd, struct cache_\n {\n \tSHA_CTX c;\n \tstruct cache_header hdr;\n-\tint i, removed;\n+\tint i, removed, recent;\n+\tstruct stat st;\n+\ttime_t now;\n \n \tfor (i = removed = 0; i < entries; i++)\n \t\tif (!cache[i]->ce_mode)\n@@ -959,15 +962,19 @@ int write_cache(int newfd, struct cache_\n \tif (ce_write(&c, newfd, &hdr, sizeof(hdr)) < 0)\n \t\treturn -1;\n \n+\tnow = fstat(newfd, &st) ? 0 : st.st_mtime;\n+\trecent = 0;\n \tfor (i = 0; i < entries; i++) {\n \t\tstruct cache_entry *ce = cache[i];\n+\t\ttime_t entry_time = (time_t) ntohl(ce->ce_mtime.sec);\n \t\tif (!ce->ce_mode)\n \t\t\tcontinue;\n-\t\tif (index_file_timestamp &&\n-\t\t    index_file_timestamp <= ntohl(ce->ce_mtime.sec))\n+\t\tif (index_file_timestamp && index_file_timestamp <= entry_time)\n \t\t\tce_smudge_racily_clean_entry(ce);\n \t\tif (ce_write(&c, newfd, ce, ce_size(ce)) < 0)\n \t\t\treturn -1;\n+\t\tif (now && now <= entry_time)\n+\t\t\trecent++;\n \t}\n \n \t/* Write extension data here */\n@@ -983,5 +990,34 @@ int write_cache(int newfd, struct cache_\n \t\t\treturn -1;\n \t\t}\n \t}\n+\n+\t/*\n+\t * To prevent later ce_match_stat() from always falling into\n+\t * check_fs(), if we have too many entries that can trigger\n+\t * racily clean check, we are better off delaying the return.\n+\t * We arbitrarily say if more than 20 paths or 25% of total\n+\t * paths are very new, we delay the return until the index\n+\t * file gets a new timestamp.\n+\t *\n+\t * NOTE! NOTE! NOTE!\n+\t *\n+\t * This assumes that nobody is touching the working tree while\n+\t * we are updating the index.\n+\t */\n+\tif (20 < recent || entries <= recent * 4) {\n+\t\tnow = fstat(newfd, &st) ? 0 : st.st_mtime;\n+\t\twhile (now && !fstat(newfd, &st) && st.st_mtime <= now) {\n+\t\t\tstruct timespec rq, rm;\n+\t\t\toff_t where = lseek(newfd, 0, SEEK_CUR);\n+\t\t\trq.tv_sec = 0;\n+\t\t\trq.tv_nsec = 250000000;\n+\t\t\tnanosleep(&rq, &rm);\n+\t\t\tif ((where == (off_t) -1) ||\n+\t\t\t    (write(newfd, \"\", 1) != 1) ||\n+\t\t\t    (lseek(newfd, -1, SEEK_CUR) != where) ||\n+\t\t\t    ftruncate(newfd, where))\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n \treturn ce_flush(&c, newfd);\n }\n-- \n1.4.2.rc3.g19a8a\n"},{"id":"24979","messageId":"Pine.LNX.4.63.0608081659320.1800@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5059","inReplyTo":"7virl7e8ed.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Racy git: avoid having to be always too careful","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-08-08T15:43:20Z","receivedAt":"2006-08-08T15:43:20Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 5 Aug 2006, Junio C Hamano wrote:\n\n> Immediately after a bulk checkout, most of the paths in the\n> working tree would have the same timestamp as the index file,\n> and this would force ce_match_stat() to take slow path for all\n> of them.  When writing an index file out, if many of the paths\n> have very new (read: the same timestamp as the index file being\n> written out) timestamp, we are better off delaying the return\n> from the command, to make sure that later command to touch the\n> working tree files will leave newer timestamps than recorded in\n> the index, thereby avoiding to take the slow path.\n\nThis makes \"make test\" dog slow. How about making this overrideable?\n(It is just a guess, but I _think_ that the sleeping is worse than having \nto check the files with the same time stamp again and again -- a \"git \nstatus\" will help that).\n\n\"make test\" without this patch:\n\n\treal    9m59.314s\n\tuser    0m36.580s\n\tsys     0m39.290s\n\n\"make test\" with this patch:\n\n\treal    1m36.429s\n\tuser    0m37.410s\n\tsys     0m40.460s\n\n-- 8< --\n[PATCH] read-cache: optionally disable being cautious with racy caches\n\nBy setting the environment variable GIT_RISK_RACY_CACHE or the config\nvariable core.riskRacyCache, the sleeping (to avoid a racy cache) is\ndisabled. This is also used for \"make test\", drastically reducing the\nneeded time.\n\nSigned-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n---\n Documentation/config.txt |    4 ++++\n Documentation/git.txt    |    5 +++++\n cache.h                  |    1 +\n config.c                 |    4 ++++\n environment.c            |    1 +\n read-cache.c             |    3 ++-\n t/test-lib.sh            |    2 ++\n 7 files changed, 19 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex d89916b..b61fd91 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -103,6 +103,10 @@ core.legacyheaders::\n \tdatabase directly (where the \"http://\" and \"rsync://\" protocols\n \tcount as direct access).\n \n+core.riskRacyCache::\n+\tA boolean which allows to disable the 1-second sleeping to avoid\n+\ta racy cache.\n+\n alias.*::\n \tCommand aliases for the gitlink:git[1] command wrapper - e.g.\n \tafter defining \"alias.last = cat-file commit HEAD\", the invocation\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex ba525d3..ff464e8 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -649,6 +649,11 @@ other\n \tstderr telling about alias expansion, built-in command\n \texecution and external command execution.\n \n+'GIT_RISK_RACY_CACHE'::\n+\tIf this variable is set, gitlink:git-read-cache[1] will never\n+\tsleep to avoid a racy cache by making the timestamps of the index\n+\tfile newer than the files referenced in the index.\n+\n Discussion[[Discussion]]\n ------------------------\n include::README[]\ndiff --git a/cache.h b/cache.h\nindex 19fdef6..e8bbe6d 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -174,6 +174,7 @@ #define REFRESH_UNMERGED\t0x0002\t/* allow\n #define REFRESH_QUIET\t\t0x0004\t/* be quiet about it */\n #define REFRESH_IGNORE_MISSING\t0x0008\t/* ignore non-existent */\n extern int refresh_cache(unsigned int flags);\n+extern int risk_racy_cache;\n \n struct lock_file {\n \tstruct lock_file *next;\ndiff --git a/config.c b/config.c\nindex c6e6f6a..4ab9329 100644\n--- a/config.c\n+++ b/config.c\n@@ -291,6 +291,10 @@ int git_default_config(const char *var, \n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.riskracycache\")) {\n+\t\trisk_racy_cache = git_config_bool(var, value);\n+\t}\n+\n \tif (!strcmp(var, \"user.name\")) {\n \t\tstrlcpy(git_default_name, value, sizeof(git_default_name));\n \t\treturn 0;\ndiff --git a/environment.c b/environment.c\nindex 1ce3411..a481fc4 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -24,6 +24,7 @@ const char *apply_default_whitespace = N\n int zlib_compression_level = Z_DEFAULT_COMPRESSION;\n int pager_in_use;\n int pager_use_color = 1;\n+int risk_racy_cache = 0;\n \n static int dyn_git_object_dir, dyn_git_index_file, dyn_git_graft_file;\n static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir,\ndiff --git a/read-cache.c b/read-cache.c\nindex d64b503..f20dac5 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -1021,7 +1021,8 @@ int write_cache(int newfd, struct cache_\n \t * This assumes that nobody is touching the working tree while\n \t * we are updating the index.\n \t */\n-\tif (20 < recent || entries <= recent * 4) {\n+\tif (!risk_racy_cache && !getenv(\"GIT_RISK_RACY_CACHE\") &&\n+\t\t\t(20 < recent || entries <= recent * 4)) {\n \t\tnow = fstat(newfd, &st) ? 0 : st.st_mtime;\n \t\twhile (now && !fstat(newfd, &st) && st.st_mtime <= now) {\n \t\t\tstruct timespec rq, rm;\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex b6d119a..3b6882d 100755\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -197,6 +197,8 @@ # t/ subdirectory and are run in trash s\n PATH=$(pwd)/..:$PATH\n GIT_EXEC_PATH=$(pwd)/..\n export PATH GIT_EXEC_PATH\n+GIT_RISK_RACY_CACHE=1\n+export GIT_RISK_RACY_CACHE\n \n # Similarly use ../compat/subprocess.py if our python does not\n # have subprocess.py on its own.\n-- \n1.4.2.rc3.g6b27\n"},{"id":"24986","messageId":"7vr6zrw352.fsf@assigned-by-dhcp.cox.net","threadId":"5059","inReplyTo":"Pine.LNX.4.63.0608081659320.1800@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] Racy git: avoid having to be always too careful","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-08-08T17:25:45Z","receivedAt":"2006-08-08T17:25:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> This makes \"make test\" dog slow. How about making this overrideable?\n> (It is just a guess, but I _think_ that the sleeping is worse than having \n> to check the files with the same time stamp again and again -- a \"git \n> status\" will help that).\n>\n>\n> -- 8< --\n> [PATCH] read-cache: optionally disable being cautious with racy caches\n>\n> By setting the environment variable GIT_RISK_RACY_CACHE or the config\n> variable core.riskRacyCache, the sleeping (to avoid a racy cache) is\n> disabled.\n\n\"RISK\" is a misnomer here -- the original code without patch is\nbeing cautious and taking a runtime hit but not risky.  The\n\"delay while writing the cache out for the first time\" is trying\nto avoid the runtime hit by taking a hit at cache generation\ntime.  In either case you never risk racy cache.\n\nPerhaps throwing away the whole \"delay\" thing might be simpler\nand more worthwhile.\n\nAnother possibility is to tweak the heuristics -- currently we\nsay 20 paths or 1/4 of whole paths is too many and would cause\ntoo much runtime hit but that was done without any measurement.\nWe could raise the threashold which would solve the case for the\ntestsuite whose trees are all small.\n"},{"id":"25001","messageId":"Pine.LNX.4.63.0608090030080.1800@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5059","inReplyTo":"7vr6zrw352.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Racy git: avoid having to be always too careful","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-08-08T22:30:40Z","receivedAt":"2006-08-08T22:30:40Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 8 Aug 2006, Junio C Hamano wrote:\n\n> Another possibility is to tweak the heuristics -- currently we\n> say 20 paths or 1/4 of whole paths is too many and would cause\n> too much runtime hit but that was done without any measurement.\n> We could raise the threashold which would solve the case for the\n> testsuite whose trees are all small.\n\nOkay, how about 100 paths? And make _this_ a config variable?\n\nCiao,\nDscho\n"},{"id":"25006","messageId":"7vpsfau8qn.fsf@assigned-by-dhcp.cox.net","threadId":"5059","inReplyTo":"Pine.LNX.4.63.0608090030080.1800@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] Racy git: avoid having to be always too careful","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-08-08T23:07:44Z","receivedAt":"2006-08-08T23:07:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Okay, how about 100 paths? And make _this_ a config variable?\n\nActually, I have a better patch I'll be pushing out in \"next\"\nthis evening.\n\nHint: ce_write() batches and buffers things, so \"now\" obtained\nin the current code is unreliable.\n"},{"id":"25009","messageId":"Pine.LNX.4.63.0608090143410.1800@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5059","inReplyTo":"7vpsfau8qn.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Racy git: avoid having to be always too careful","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-08-08T23:44:09Z","receivedAt":"2006-08-08T23:44:09Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 8 Aug 2006, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > Okay, how about 100 paths? And make _this_ a config variable?\n> \n> Actually, I have a better patch I'll be pushing out in \"next\"\n> this evening.\n\nPity. I just whipped up this patch...\n\n-- 8< --\n[PATCH] read-cache: introduce core.racyThreshold\n\nThe expensive sleep is done only when there are more than 100 updated\nfiles now. This number is overrideable by the core.racyThreshold variable.\n\nSigned-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n---\n Documentation/config.txt |    5 +++++\n cache.h                  |    1 +\n config.c                 |    5 +++++\n environment.c            |    1 +\n read-cache.c             |    2 +-\n 5 files changed, 13 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex d89916b..f428fb5 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -103,6 +103,11 @@ core.legacyheaders::\n \tdatabase directly (where the \"http://\" and \"rsync://\" protocols\n \tcount as direct access).\n \n+core.racyThreshold::\n+\tThe number of files which need to be updated at least, before\n+\tread-cache sleeps to make the timestamps of these files and the\n+\ttimestamp of the index file distinct.\n+\n alias.*::\n \tCommand aliases for the gitlink:git[1] command wrapper - e.g.\n \tafter defining \"alias.last = cat-file commit HEAD\", the invocation\ndiff --git a/cache.h b/cache.h\nindex 19fdef6..8c0a830 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -193,6 +193,7 @@ extern int warn_ambiguous_refs;\n extern int shared_repository;\n extern const char *apply_default_whitespace;\n extern int zlib_compression_level;\n+extern int racy_threshold;\n \n #define GIT_REPO_VERSION 0\n extern int repository_format_version;\ndiff --git a/config.c b/config.c\nindex c6e6f6a..01b8c23 100644\n--- a/config.c\n+++ b/config.c\n@@ -291,6 +291,11 @@ int git_default_config(const char *var, \n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.racythreshold\")) {\n+\t\tracy_threshold = git_config_int(var, value);\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(var, \"user.name\")) {\n \t\tstrlcpy(git_default_name, value, sizeof(git_default_name));\n \t\treturn 0;\ndiff --git a/environment.c b/environment.c\nindex 1ce3411..3656f36 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -24,6 +24,7 @@ const char *apply_default_whitespace = N\n int zlib_compression_level = Z_DEFAULT_COMPRESSION;\n int pager_in_use;\n int pager_use_color = 1;\n+int racy_threshold = 100;\n \n static int dyn_git_object_dir, dyn_git_index_file, dyn_git_graft_file;\n static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir,\ndiff --git a/read-cache.c b/read-cache.c\nindex d64b503..42a3f78 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -1021,7 +1021,7 @@ int write_cache(int newfd, struct cache_\n \t * This assumes that nobody is touching the working tree while\n \t * we are updating the index.\n \t */\n-\tif (20 < recent || entries <= recent * 4) {\n+\tif (racy_threshold < recent) {\n \t\tnow = fstat(newfd, &st) ? 0 : st.st_mtime;\n \t\twhile (now && !fstat(newfd, &st) && st.st_mtime <= now) {\n \t\t\tstruct timespec rq, rm;\n-- \n1.4.2.rc3.gfa72-dirty\n"}]}