Skip to content

Commit

Permalink
Reimplement async procedures using pthreads
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Johannes Sixt authored and Junio C Hamano committed Mar 7, 2010
1 parent 912b263 commit 200a76b
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 20 deletions.
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 \
Expand Down Expand Up @@ -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
Expand Down
41 changes: 23 additions & 18 deletions run-command.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,14 @@ 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)
{
int flags = fcntl(fd, F_GETFD);
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)
{
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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);

Expand All @@ -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;
Expand All @@ -549,17 +556,15 @@ int start_async(struct async *async)

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, ...)
Expand Down
8 changes: 6 additions & 2 deletions run-command.h
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 200a76b

Please sign in to comment.