Git fork

grep: work around LSan threading race with barrier

There's a race with LSan when spawning threads and one of the threads
calls die(). We worked around one such problem with index-pack in the
previous commit, but it exists in git-grep, too. You can see it with:

make SANITIZE=leak THREAD_BARRIER_PTHREAD=YesOnLinux
cd t
./t0003-attributes.sh --stress

which fails pretty quickly with:

==git==4096424==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 32 byte(s) in 1 object(s) allocated from:
#0 0x7f906de14556 in realloc ../../../../src/libsanitizer/lsan/lsan_interceptors.cpp:98
#1 0x7f906dc9d2c1 in __pthread_getattr_np nptl/pthread_getattr_np.c:180
#2 0x7f906de2500d in __sanitizer::GetThreadStackTopAndBottom(bool, unsigned long*, unsigned long*) ../../../../src/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp:150
#3 0x7f906de25187 in __sanitizer::GetThreadStackAndTls(bool, unsigned long*, unsigned long*, unsigned long*, unsigned long*) ../../../../src/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp:614
#4 0x7f906de17d18 in __lsan::ThreadStart(unsigned int, unsigned long long, __sanitizer::ThreadType) ../../../../src/libsanitizer/lsan/lsan_posix.cpp:53
#5 0x7f906de143a9 in ThreadStartFunc<false> ../../../../src/libsanitizer/lsan/lsan_interceptors.cpp:431
#6 0x7f906dc9bf51 in start_thread nptl/pthread_create.c:447
#7 0x7f906dd1a677 in __clone3 ../sysdeps/unix/sysv/linux/x86_64/clone3.S:78

As with the previous commit, we can fix this by inserting a barrier that
makes sure all threads have finished their setup before continuing. But
there's one twist in this case: the thread which calls die() is not one
of the worker threads, but the main thread itself!

So we need the main thread to wait in the barrier, too, until all
threads have gotten to it. And thus we initialize the barrier for
num_threads+1, to account for all of the worker threads plus the main
one.

If we then test as above, t0003 should run indefinitely.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Jeff King and committed by
Junio C Hamano
7a8d9efc 526c0a85

+8
+8
builtin/grep.c
··· 101 /* Signalled when we are finished with everything. */ 102 static pthread_cond_t cond_result; 103 104 static int skip_first_line; 105 106 static void add_work(struct grep_opt *opt, struct grep_source *gs) ··· 198 int hit = 0; 199 struct grep_opt *opt = arg; 200 201 while (1) { 202 struct work_item *w = get_work(); 203 if (!w) ··· 229 pthread_cond_init(&cond_add, NULL); 230 pthread_cond_init(&cond_write, NULL); 231 pthread_cond_init(&cond_result, NULL); 232 grep_use_locks = 1; 233 enable_obj_read_lock(); 234 ··· 248 die(_("grep: failed to create thread: %s"), 249 strerror(err)); 250 } 251 } 252 253 static int wait_all(void) ··· 284 pthread_cond_destroy(&cond_add); 285 pthread_cond_destroy(&cond_write); 286 pthread_cond_destroy(&cond_result); 287 grep_use_locks = 0; 288 disable_obj_read_lock(); 289
··· 101 /* Signalled when we are finished with everything. */ 102 static pthread_cond_t cond_result; 103 104 + /* Synchronize the start of all threads */ 105 + static maybe_thread_barrier_t start_barrier; 106 + 107 static int skip_first_line; 108 109 static void add_work(struct grep_opt *opt, struct grep_source *gs) ··· 201 int hit = 0; 202 struct grep_opt *opt = arg; 203 204 + maybe_thread_barrier_wait(&start_barrier); 205 + 206 while (1) { 207 struct work_item *w = get_work(); 208 if (!w) ··· 234 pthread_cond_init(&cond_add, NULL); 235 pthread_cond_init(&cond_write, NULL); 236 pthread_cond_init(&cond_result, NULL); 237 + maybe_thread_barrier_init(&start_barrier, NULL, num_threads + 1); 238 grep_use_locks = 1; 239 enable_obj_read_lock(); 240 ··· 254 die(_("grep: failed to create thread: %s"), 255 strerror(err)); 256 } 257 + maybe_thread_barrier_wait(&start_barrier); 258 } 259 260 static int wait_all(void) ··· 291 pthread_cond_destroy(&cond_add); 292 pthread_cond_destroy(&cond_write); 293 pthread_cond_destroy(&cond_result); 294 + maybe_thread_barrier_destroy(&start_barrier); 295 grep_use_locks = 0; 296 disable_obj_read_lock(); 297