run-command: use thread-aware die_is_recursing routine

If we die from an async thread, we do not actually exit the program, but just kill the thread. This confuses the static counter in usage.c's default die_is_recursing function; it updates the counter once for the thread death, and then when the main program calls die() itself, it erroneously thinks we are recursing. The end result is that we print "recursion detected in die handler" instead of the real error in such a case (the easiest way to trigger this is having a remote connection hang up while running a sideband demultiplexer). This patch solves it by using a per-thread counter when the async_die function is installed; we detect recursion in each thread (including the main one), but they do not step on each other's toes. Other threaded code does not need to worry about this, as they do not install specialized die handlers; they just let a die() from a sub-thread take down the whole program. Since we are overriding the default recursion-check function, there is an interesting corner case that is not a problem, but bears some explanation. Imagine the main thread calls die(), and then in the die_routine starts an async call. We will switch to using thread-local storage, which starts at 0, for the main thread's counter, even though the original counter was actually at 1. That's OK, though, for two reasons: 1. It would miss only the first level of recursion, and would still find recursive failures inside the async helper. 2. We do not currently and are not likely to start doing anything as heavyweight as starting an async routine from within a die routine or helper function. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Apr 16, 2013 at 15:50 UTC 1ece66bc9e5433dff008786daba9918f3e2a6525
1 file changed +11
run-command.c
+11
@@ -583,6 +583,7 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
583 static pthread_t main_thread;
584 static int main_thread_set;
585 static pthread_key_t async_key;
586 +static pthread_key_t async_die_counter;
587
588 static void *run_thread(void *data)
589 {
@@ -609,6 +610,14 @@ static NORETURN void die_async(const char *err, va_list params)
610
611 exit(128);
612 }
613 +
614 +static int async_die_is_recursing(void)
615 +{
616 + void *ret = pthread_getspecific(async_die_counter);
617 + pthread_setspecific(async_die_counter, (void *)1);
618 + return ret != NULL;
619 +}
620 +
621 #endif
622
623 int start_async(struct async *async)
@@ -690,7 +699,9 @@ int start_async(struct async *async)
699 main_thread_set = 1;
700 main_thread = pthread_self();
701 pthread_key_create(&async_key, NULL);
702 + pthread_key_create(&async_die_counter, NULL);
703 set_die_routine(die_async);
704 + set_die_is_recursing_routine(async_die_is_recursing);
705 }
706
707 if (proc_in >= 0)