Skip to content

Commit 0ea1c89

Browse files
j6tgitster
authored andcommitted
Dying in an async procedure should only exit the thread, not the process.
Async procedures are intended as helpers that perform a very restricted task, and the caller usually has to manage them in a larger context. Conceptually, the async procedure is not concerned with the "bigger picture" in whose context it is run. When it dies, it is not supposed to destroy this "bigger picture", but rather only its own limit view of the world. On POSIX, the async procedure is run in its own process, and exiting this process naturally had only these limited effects. On Windows (or when ASYNC_AS_THREAD is set), calling die() exited the whole process, destroying the caller (the "big picture") as well. This fixes it to exit only the thread. Without ASYNC_AS_THREAD, one particular effect of exiting the async procedure process is that it automatically closes file descriptors, most notably the writable end of the pipe that the async procedure writes to. The async API already requires that the async procedure closes the pipe ends when it exits normally. But for calls to die() no requirements are imposed. In the non-threaded case the pipe ends are closed implicitly by the exiting process, but in the threaded case, the die routine must take care of closing them. Now t5530-upload-pack-error.sh passes on Windows. Signed-off-by: Johannes Sixt <j6t@kdbg.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 200a76b commit 0ea1c89

1 file changed

Lines changed: 34 additions & 0 deletions

File tree

run-command.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,12 +448,35 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
448448
}
449449

450450
#ifdef ASYNC_AS_THREAD
451+
static pthread_t main_thread;
452+
static int main_thread_set;
453+
static pthread_key_t async_key;
454+
451455
static void *run_thread(void *data)
452456
{
453457
struct async *async = data;
458+
459+
pthread_setspecific(async_key, async);
460+
454461
intptr_t ret = async->proc(async->proc_in, async->proc_out, async->data);
455462
return (void *)ret;
456463
}
464+
465+
static NORETURN void die_async(const char *err, va_list params)
466+
{
467+
vreportf("fatal: ", err, params);
468+
469+
if (!pthread_equal(main_thread, pthread_self())) {
470+
struct async *async = pthread_getspecific(async_key);
471+
if (async->proc_in >= 0)
472+
close(async->proc_in);
473+
if (async->proc_out >= 0)
474+
close(async->proc_out);
475+
pthread_exit((void *)128);
476+
}
477+
478+
exit(128);
479+
}
457480
#endif
458481

459482
int start_async(struct async *async)
@@ -525,6 +548,17 @@ int start_async(struct async *async)
525548
else if (async->out)
526549
close(async->out);
527550
#else
551+
if (!main_thread_set) {
552+
/*
553+
* We assume that the first time that start_async is called
554+
* it is from the main thread.
555+
*/
556+
main_thread_set = 1;
557+
main_thread = pthread_self();
558+
pthread_key_create(&async_key, NULL);
559+
set_die_routine(die_async);
560+
}
561+
528562
if (proc_in >= 0)
529563
set_cloexec(proc_in);
530564
if (proc_out >= 0)

0 commit comments

Comments
 (0)