Reimplement async procedures using pthreads
authorJohannes Sixt <j6t@kdbg.org>
Sat, 6 Mar 2010 15:40:42 +0000 (16:40 +0100)
committerJunio C Hamano <gitster@pobox.com>
Sun, 7 Mar 2010 08:37:36 +0000 (00:37 -0800)
On Windows, async procedures have always been run in threads, and the
implementation used Windows specific APIs. Rewrite the code to use pthreads.

A new configuration option is introduced so that the threaded implementation
can also be used on POSIX systems. Since this option is intended only as
playground on POSIX, but is mandatory on Windows, the option is not
documented.

One detail is that on POSIX it is necessary to set FD_CLOEXEC on the pipe
handles. On Windows, this is not needed because pipe handles are not
inherited to child processes, and the new calls to set_cloexec() are
effectively no-ops.

Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Makefile
run-command.c
run-command.h

index 52f2cc040ba82696b199537d3155a095939dac68..2fe52f8163e67fc593ca6ffc75d584bf230c3667 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -979,6 +979,7 @@ ifeq ($(uname_S),Windows)
        NO_CURL = YesPlease
        NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
+       ASYNC_AS_THREAD = YesPlease
 
        CC = compat/vcbuild/scripts/clink.pl
        AR = compat/vcbuild/scripts/lib.pl
@@ -1030,6 +1031,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_REGEX = YesPlease
        NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
+       ASYNC_AS_THREAD = YesPlease
        COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch -Icompat/win32
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
        COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o \
@@ -1342,6 +1344,9 @@ ifdef NO_PTHREADS
 else
        EXTLIBS += $(PTHREAD_LIBS)
        LIB_OBJS += thread-utils.o
+ifdef ASYNC_AS_THREAD
+       BASIC_CFLAGS += -DASYNC_AS_THREAD
+endif
 endif
 
 ifdef DIR_HAS_BSD_GROUP_SEMANTICS
index 0cd7f02ffe597d3708873d4f3d6b4c0e88e8fae7..77aefff2e27e369feffccbad817845af17597b02 100644 (file)
@@ -82,6 +82,7 @@ static NORETURN void die_child(const char *err, va_list params)
        write(child_err, "\n", 1);
        exit(128);
 }
+#endif
 
 static inline void set_cloexec(int fd)
 {
@@ -89,7 +90,6 @@ static inline void set_cloexec(int fd)
        if (flags >= 0)
                fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
 }
-#endif
 
 static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure)
 {
@@ -447,11 +447,12 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
        return run_command(&cmd);
 }
 
-#ifdef WIN32
-static unsigned __stdcall run_thread(void *data)
+#ifdef ASYNC_AS_THREAD
+static void *run_thread(void *data)
 {
        struct async *async = data;
-       return async->proc(async->proc_in, async->proc_out, async->data);
+       intptr_t ret = async->proc(async->proc_in, async->proc_out, async->data);
+       return (void *)ret;
 }
 #endif
 
@@ -497,7 +498,7 @@ int start_async(struct async *async)
        else
                proc_out = -1;
 
-#ifndef WIN32
+#ifndef ASYNC_AS_THREAD
        /* Flush stdio before fork() to avoid cloning buffers */
        fflush(NULL);
 
@@ -524,12 +525,18 @@ int start_async(struct async *async)
        else if (async->out)
                close(async->out);
 #else
+       if (proc_in >= 0)
+               set_cloexec(proc_in);
+       if (proc_out >= 0)
+               set_cloexec(proc_out);
        async->proc_in = proc_in;
        async->proc_out = proc_out;
-       async->tid = (HANDLE) _beginthreadex(NULL, 0, run_thread, async, 0, NULL);
-       if (!async->tid) {
-               error("cannot create thread: %s", strerror(errno));
-               goto error;
+       {
+               int err = pthread_create(&async->tid, NULL, run_thread, async);
+               if (err) {
+                       error("cannot create thread: %s", strerror(err));
+                       goto error;
+               }
        }
 #endif
        return 0;
@@ -549,17 +556,15 @@ error:
 
 int finish_async(struct async *async)
 {
-#ifndef WIN32
-       int ret = wait_or_whine(async->pid, "child process", 0);
+#ifndef ASYNC_AS_THREAD
+       return wait_or_whine(async->pid, "child process", 0);
 #else
-       DWORD ret = 0;
-       if (WaitForSingleObject(async->tid, INFINITE) != WAIT_OBJECT_0)
-               ret = error("waiting for thread failed: %lu", GetLastError());
-       else if (!GetExitCodeThread(async->tid, &ret))
-               ret = error("cannot get thread exit code: %lu", GetLastError());
-       CloseHandle(async->tid);
+       void *ret = (void *)(intptr_t)(-1);
+
+       if (pthread_join(async->tid, &ret))
+               error("pthread_join failed");
+       return (int)(intptr_t)ret;
 #endif
-       return ret;
 }
 
 int run_hook(const char *index_file, const char *name, ...)
index 94619f52d95888b320664b7f19db3eeb7d6d8cca..40db39cec0d4dcaac4de6e34d8734be14270552a 100644 (file)
@@ -1,6 +1,10 @@
 #ifndef RUN_COMMAND_H
 #define RUN_COMMAND_H
 
+#ifdef ASYNC_AS_THREAD
+#include <pthread.h>
+#endif
+
 struct child_process {
        const char **argv;
        pid_t pid;
@@ -74,10 +78,10 @@ struct async {
        void *data;
        int in;         /* caller writes here and closes it */
        int out;        /* caller reads from here and closes it */
-#ifndef WIN32
+#ifndef ASYNC_AS_THREAD
        pid_t pid;
 #else
-       HANDLE tid;
+       pthread_t tid;
        int proc_in;
        int proc_out;
 #endif