grep threading cleanup & tests
v2 cover grep threading cleanup & tests
This is the spiritual successor to the "grep: add ability to disable threading with --threads=0 or grep.threads=0" patch I submitted as part of my PCRE series (<[email protected]>).
There's a long back & forth thread between me and Jeff King as a follow-up to that which I'll summarize: I'd misunderstood what --threads=1 does, because I was tracing how often a pattern was compiled, and observing that even with --threads=1 we'd compile the pattern twice.
This series fixes that redundant needless compilation in 3/8, skips pthread overhead as Jeff suggested kin 3/8. The rest of the patches are a bunch of small related fixes I noticed along the way.
Ævar Arnfjörð Bjarmason (8):
grep: assert that threading is enabled when calling grep_{lock,unlock}
grep: add tests for --threads=N and grep.threads
grep: don't redundantly compile throwaway patterns under threading
grep: skip pthreads overhead when using one thread
tests: add a PTHREADS prerequisite
pack-object & index-pack: add test for --threads warning under
NO_PTHREADS
pack-objects: fix buggy warning about threads under
NO_PTHREADS=YesPlease
grep: given --threads with NO_PTHREADS=YesPlease, warn
Makefile | 1 + builtin/grep.c | 32 +++++++++++++++++++++++++------- builtin/pack-objects.c | 4 +++- t/README | 4 ++++ t/t5300-pack-object.sh | 33 +++++++++++++++++++++++++++++++++ t/t7810-grep.sh | 34 ++++++++++++++++++++++++++++++++++ t/test-lib.sh | 1 + 7 files changed, 101 insertions(+), 8 deletions(-)
Signature · 1 line
2.11.0
PATCH v2 1/8 grep: assert that threading is enabled when calling grep_{lock,unlock}
Change the grep_{lock,unlock} functions to assert that num_threads is true, instead of only locking & unlocking the pthread mutex lock when it is.
These functions are never called when num_threads isn't true, this logic has gone through multiple iterations since the initial introduction of grep threading in commit 5b594f457a ("Threaded grep", 2010-01-25), but ever since then they'd only be called if num_threads was true, so this check made the code confusing to read.
Replace the check with an assertion, so that it's clear to the reader that this code path is never taken unless we're spawning threads.
--- builtin/grep.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
builtin/grep.c +4 -4
@@ -73,14 +73,14 @@ static pthread_mutex_t grep_mutex;static inline void grep_lock (void ) { -if (num_threads) -pthread_mutex_lock (&grep_mutex); +assert (num_threads); +pthread_mutex_lock (&grep_mutex); }static inline void grep_unlock (void ) { -if (num_threads) -pthread_mutex_unlock (&grep_mutex); +assert (num_threads); +pthread_mutex_unlock (&grep_mutex); }/* Signalled when a new work_item is added to todo. */
Signature · 1 line
2.11.0
PATCH v2 2/8 grep: add tests for --threads=N and grep.threads
In reply to Ævar Arnfjörð BjarmasonAdd tests for when --threads=N is supplied on the command-line or when grep.threads is supplied in the configuration.
When the threading support was made run-time configurable in commit 89f09dd34e ("grep: add --threads=<num> option and grep.threads configuration", 2015-12-15) no tests were added for it.
In developing a change to grep I was able to make '--threads=1 <pat>` segfault, while the test suite still passed. This change fixes that blind spot in the tests.
--- t/t7810-grep.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
t/t7810-grep.sh +16 -0
@@ -771,6 +771,22 @@ test_expect_success 'grep -W with userdiff' ' test_cmp expected actual ' +for threads in $(test_seq 0 10) +do + test_expect_success "grep --threads=$threads & -c grep.threads=$threads" " + git grep --threads=$threads . >actual.$threads && + if test $threads -ge 1 + then + test_cmp actual.\$(($threads - 1)) actual.$threads + fi && + git -c grep.threads=$threads grep . >actual.$threads && + if test $threads -ge 1 + then + test_cmp actual.\$(($threads - 1)) actual.$threads + fi + " +done + test_expect_success 'grep from a subdirectory to search wider area (1)' ' mkdir -p s && (
Signature · 1 line
2.11.0
PATCH v2 3/8 grep: don't redundantly compile throwaway patterns under threading
In reply to Ævar Arnfjörð BjarmasonChange the pattern compilation logic under threading so that grep doesn't compile a pattern it never ends up using on the non-threaded code path, only to compile it again N times for N threads which will each use their own copy, ignoring the initially compiled pattern.
This redundant compilation dates back to the initial introduction of the threaded grep in commit 5b594f457a ("Threaded grep", 2010-01-25).
There was never any reason for doing this redundant work other than an oversight in the initial commit. Jeff King suggested on-list in <[email protected]> that this might be needed to check the pattern for sanity before threaded execution commences.
That's not the case. The pattern is compiled under threading in start_threads() before any concurrent execution has started by calling pthread_create(), so if the pattern contains an error we still do the right thing. I.e. die with one error before any threaded execution has commenced, instead of e.g. spewing out an error for each N threads, which could be a regression a change like this might inadvertently introduce.
The undocumented --debug mode added in commit 17bf35a3c7 ("grep: teach --debug option to dump the parse tree", 2012-09-13) still works properly with this change. It only emits debugging info during pattern compilation, which is now dumped by the pattern compiled just before the first thread is started.
--- builtin/grep.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
builtin/grep.c +11 -3
@@ -224,7 +224,8 @@ static void start_threads(struct grep_opt *opt)int err;struct grep_opt *o =grep_opt_dup (opt); o->output = strbuf_out; - o->debug =0 ; +if (i) + o->debug =0 ;compile_grep_patterns (o); err =pthread_create (&threads[i],NULL , run, o);
@@ -1154,8 +1155,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)if (!opt.fixed && opt.ignore_case) opt.regflags |=REG_ICASE ; -compile_grep_patterns (&opt); -/* * We have to find "--" in a separate pass, because its presence * influences how we will parse arguments that come before it.
@@ -1230,6 +1229,15 @@ int cmd_grep(int argc, const char **argv, const char *prefix) num_threads =0 ;#endif +if (!num_threads) +/* +* The compiled patterns on the main path are only +* used when not using threading. Otherwise +* start_threads() below calls compile_grep_patterns() +* for each thread. +*/ +compile_grep_patterns (&opt); +#ifndef NO_PTHREADS if (num_threads) { if (!(opt.name_only || opt.unmatch_name_only || opt.count)
Signature · 1 line
2.11.0
PATCH v2 4/8 grep: skip pthreads overhead when using one thread
In reply to Ævar Arnfjörð BjarmasonSkip the administrative overhead of using pthreads when only using one thread. Instead take the non-threaded path which would be taken under NO_PTHREADS.
The threading support was initially added in commit 5b594f457a ("Threaded grep", 2010-01-25) with a hardcoded compile-time number of 8 threads. Later the number of threads was made configurable in commit 89f09dd34e ("grep: add --threads=<num> option and grep.threads configuration", 2015-12-15).
That change did not add any special handling for --threads=1. Now we take a slightly faster path by skipping thread handling entirely when 1 thread is requested.
--- builtin/grep.c | 2 ++ 1 file changed, 2 insertions(+)
builtin/grep.c +2 -0
@@ -1225,6 +1225,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) num_threads =GREP_NUM_THREADS_DEFAULT ; else if (num_threads < 0) die(_("invalid number of threads specified (%d)"), num_threads); +if (num_threads ==1 ) + num_threads =0 ;#else num_threads =0 ;#endif
Signature · 1 line
2.11.0
PATCH v2 5/8 tests: add a PTHREADS prerequisite
In reply to Ævar Arnfjörð BjarmasonAdd a PTHREADS prerequisite which is false when git is compiled with NO_PTHREADS=YesPlease.
There's lots of custom code that runs when threading isn't available, but before this prerequisite there was no way to test it.
--- Makefile | 1 + t/README | 4 ++++ t/test-lib.sh | 1 + 3 files changed, 6 insertions(+)
Makefile +1 -0
@@ -2235,6 +2235,7 @@ GIT-BUILD-OPTIONS: FORCE @echo NO_EXPAT=\''$(subst ','\'',$(subst ','\'',$(NO_EXPAT)))'\' >>$@+ @echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@+ @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+ + @echo NO_PTHREADS=\''$(subst ','\'',$(subst ','\'',$(NO_PTHREADS)))'\' >>$@+ @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+ @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+ @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+
t/README +4 -0
@@ -817,6 +817,10 @@ use these, and "test_set_prereq" for how to define your own. Test is run on a filesystem which converts decomposed utf-8 (nfd) to precomposed utf-8 (nfc). + - PTHREADS + + Git wasn't compiled with NO_PTHREADS=YesPlease. + Tips for Writing Tests ----------------------
t/test-lib.sh +1 -0
@@ -1009,6 +1009,7 @@ esac ( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1 test -z "$NO_PERL" && test_set_prereq PERL +test -z "$NO_PTHREADS" && test_set_prereq PTHREADS test -z "$NO_PYTHON" && test_set_prereq PYTHON test -n "$USE_LIBPCRE" && test_set_prereq LIBPCRE test -z "$NO_GETTEXT" && test_set_prereq GETTEXT
Signature · 1 line
2.11.0
PATCH v2 6/8 pack-object & index-pack: add test for --threads warning under NO_PTHREADS
In reply to Ævar Arnfjörð BjarmasonAdd a test for the warning that's emitted when --threads or pack.threads is provided under NO_PTHREADS. This uses the new PTHREADS prerequisite.
The assertion for !GETTEXT_POISON in the latter test is currently redundant, since unlike index-pack the pack-objects warnings aren't i18n'd. However they might be changed to be i18n'd in the future, and there's no harm in future-proofing the test.
There's an existing bug in the implementation of pack-objects which this test currently tests for as-is. Details about the bug & the fix are included in a follow-up change.
--- t/t5300-pack-object.sh | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+)
t/t5300-pack-object.sh +34 -0
@@ -421,6 +421,40 @@ test_expect_success 'index-pack <pack> works in non-repo' ' test_path_is_file foo.idx ' +test_expect_success !PTHREADS,!GETTEXT_POISON 'index-pack --threads=N or pack.threads=N warns when no pthreads' ' + test_must_fail git index-pack --threads=2 2>err && + grep ^warning: err >warnings && + test_line_count = 1 warnings && + grep "no threads support, ignoring --threads=2" err && + test_must_fail git -c pack.threads=2 index-pack 2>err && + grep ^warning: err >warnings && + test_line_count = 1 warnings && + grep "no threads support, ignoring pack\.threads" err && + test_must_fail git -c pack.threads=2 index-pack --threads=4 2>err && + grep ^warning: err >warnings && + test_line_count = 2 warnings && + grep "no threads support, ignoring --threads=4" err && + grep "no threads support, ignoring pack\.threads" err +' + +test_expect_success !PTHREADS,!GETTEXT_POISON 'pack-objects --threads=N or pack.threads=N warns when no pthreads' ' + git pack-objects --threads=2 --stdout --all </dev/null >/dev/null 2>err && + grep ^warning: err >warnings && + test_line_count = 1 warnings && + grep "no threads support, ignoring --threads" err && + git -c pack.threads=2 pack-objects --stdout --all </dev/null >/dev/null 2>err && + cat err && + grep ^warning: err >warnings && + test_line_count = 2 warnings && + grep "no threads support, ignoring --threads" err && + grep "no threads support, ignoring pack\.threads" err && + git -c pack.threads=2 pack-objects --threads=4 --stdout --all </dev/null >/dev/null 2>err && + grep ^warning: err >warnings && + test_line_count = 2 warnings && + grep "no threads support, ignoring --threads" err && + grep "no threads support, ignoring pack\.threads" err +' + # # WARNING! #
Signature · 1 line
2.11.0
PATCH v2 7/8 pack-objects: fix buggy warning about threads under NO_PTHREADS=YesPlease
In reply to Ævar Arnfjörð BjarmasonFix a buggy warning about threads under NO_PTHREADS=YesPlease. Due to re-using the delta_search_threads variable for both the state of the "pack.threads" config & the --threads option setting "pack.threads" but not supplying --threads would trigger the warning for both "pack.threads" & --threads.
Solve this bug by resetting the delta_search_threads variable in git_pack_config(), it might then be set by --threads again and be subsequently warned about, as the test I'm changing here asserts.
--- builtin/pack-objects.c | 4 +++- t/t5300-pack-object.sh | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-)
builtin/pack-objects.c +3 -1
@@ -2472,8 +2472,10 @@ static int git_pack_config(const char *k, const char *v, void *cb)die ("invalid number of threads specified (%d)" , delta_search_threads);#ifdef NO_PTHREADS -if (delta_search_threads !=1 ) +if (delta_search_threads !=1 ) {warning ("no threads support, ignoring %s" , k); + delta_search_threads =0 ; + }#endif return 0 ; }
t/t5300-pack-object.sh +1 -2
@@ -445,8 +445,7 @@ test_expect_success !PTHREADS,!GETTEXT_POISON 'pack-objects --threads=N or pack. git -c pack.threads=2 pack-objects --stdout --all </dev/null >/dev/null 2>err && cat err && grep ^warning: err >warnings && - test_line_count = 2 warnings && - grep "no threads support, ignoring --threads" err && + test_line_count = 1 warnings && grep "no threads support, ignoring pack\.threads" err && git -c pack.threads=2 pack-objects --threads=4 --stdout --all </dev/null >/dev/null 2>err && grep ^warning: err >warnings &&
Signature · 1 line
2.11.0
PATCH v2 8/8 grep: given --threads with NO_PTHREADS=YesPlease, warn
In reply to Ævar Arnfjörð BjarmasonAdd a warning about missing thread support when grep.threads or --threads is set to a non 0 (default) or 1 (no parallelism) value under NO_PTHREADS=YesPlease.
This is for consistency with the index-pack & pack-objects commands, which also take a --threads option & are configurable via pack.threads, and have long warned about the same under NO_PTHREADS=YesPlease.
--- builtin/grep.c | 8 ++++++++ t/t7810-grep.sh | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+)
builtin/grep.c +8 -0
@@ -290,6 +290,12 @@ static int grep_cmd_config(const char *var, const char *value, void *cb)if (num_threads <0 )die (_ ("invalid number of threads specified (%d) for %s" ), num_threads, var); +#ifdef NO_PTHREADS + else if (num_threads && num_threads != 1) { +warning (_ ("no threads support, ignoring %s" ), var); + num_threads =0 ; + } +#endif }return st;
@@ -1228,6 +1234,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)if (num_threads ==1 ) num_threads =0 ;#else +if (num_threads) +warning (_ ("no threads support, ignoring --threads" )); num_threads =0 ;#endif
t/t7810-grep.sh +18 -0
@@ -787,6 +787,24 @@ do " done +test_expect_success !PTHREADS,!GETTEXT_POISON 'grep --threads=N or pack.threads=N warns when no pthreads' ' + git grep --threads=2 Hello hello_world 2>err && + grep ^warning: err >warnings && + test_line_count = 1 warnings && + grep "no threads support, ignoring --threads" err && + git -c grep.threads=2 grep Hello hello_world 2>err && + grep ^warning: err >warnings && + test_line_count = 1 warnings && + grep "no threads support, ignoring grep\.threads" err && + git -c grep.threads=2 grep --threads=4 Hello hello_world 2>err && + grep ^warning: err >warnings && + test_line_count = 2 warnings && + grep "no threads support, ignoring --threads" err && + grep "no threads support, ignoring grep\.threads" err && + git -c grep.threads=0 grep --threads=0 Hello hello_world 2>err && + test_line_count = 0 err +' + test_expect_success 'grep from a subdirectory to search wider area (1)' ' mkdir -p s && (
Signature · 1 line
2.11.0