Merge branch 'js/async-thread'

* js/async-thread:
  fast-import: die_nicely() back to vsnprintf (reverts part of ebaa79f)
  Enable threaded async procedures whenever pthreads is available
  Dying in an async procedure should only exit the thread, not the process.
  Reimplement async procedures using pthreads
  Windows: more pthreads functions
  Fix signature of fcntl() compatibility dummy
  Make report() from usage.c public as vreportf() and use it.
  Modernize t5530-upload-pack-error.

Conflicts:
	http-backend.c
This commit is contained in:
Junio C Hamano
2010-06-21 06:02:45 -07:00
10 changed files with 117 additions and 41 deletions

View File

@ -231,8 +231,9 @@ The function pointer in .proc has the following signature:
There are serious restrictions on what the asynchronous function can do There are serious restrictions on what the asynchronous function can do
because this facility is implemented by a pipe to a forked process on because this facility is implemented by a thread in the same address
UNIX, but by a thread in the same address space on Windows: space on most platforms (when pthreads is available), but by a pipe to
a forked process otherwise:
. It cannot change the program's state (global variables, environment, . It cannot change the program's state (global variables, environment,
etc.) in a way that the caller notices; in other words, .in and .out etc.) in a way that the caller notices; in other words, .in and .out

View File

@ -89,7 +89,7 @@ static inline int getuid()
{ return 1; } { return 1; }
static inline struct passwd *getpwnam(const char *name) static inline struct passwd *getpwnam(const char *name)
{ return NULL; } { return NULL; }
static inline int fcntl(int fd, int cmd, long arg) static inline int fcntl(int fd, int cmd, ...)
{ {
if (cmd == F_GETFD || cmd == F_SETFD) if (cmd == F_GETFD || cmd == F_SETFD)
return 0; return 0;

View File

@ -16,6 +16,7 @@
static unsigned __stdcall win32_start_routine(void *arg) static unsigned __stdcall win32_start_routine(void *arg)
{ {
pthread_t *thread = arg; pthread_t *thread = arg;
thread->tid = GetCurrentThreadId();
thread->arg = thread->start_routine(thread->arg); thread->arg = thread->start_routine(thread->arg);
return 0; return 0;
} }
@ -49,6 +50,13 @@ int win32_pthread_join(pthread_t *thread, void **value_ptr)
} }
} }
pthread_t pthread_self(void)
{
pthread_t t = { 0 };
t.tid = GetCurrentThreadId();
return t;
}
int pthread_cond_init(pthread_cond_t *cond, const void *unused) int pthread_cond_init(pthread_cond_t *cond, const void *unused)
{ {
cond->waiters = 0; cond->waiters = 0;

View File

@ -58,6 +58,7 @@ typedef struct {
HANDLE handle; HANDLE handle;
void *(*start_routine)(void*); void *(*start_routine)(void*);
void *arg; void *arg;
DWORD tid;
} pthread_t; } pthread_t;
extern int pthread_create(pthread_t *thread, const void *unused, extern int pthread_create(pthread_t *thread, const void *unused,
@ -71,4 +72,28 @@ extern int pthread_create(pthread_t *thread, const void *unused,
extern int win32_pthread_join(pthread_t *thread, void **value_ptr); extern int win32_pthread_join(pthread_t *thread, void **value_ptr);
#define pthread_equal(t1, t2) ((t1).tid == (t2).tid)
extern pthread_t pthread_self(void);
static inline int pthread_exit(void *ret)
{
ExitThread((DWORD)ret);
}
typedef DWORD pthread_key_t;
static inline int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *value))
{
return (*keyp = TlsAlloc()) == TLS_OUT_OF_INDEXES ? EAGAIN : 0;
}
static inline int pthread_setspecific(pthread_key_t key, const void *value)
{
return TlsSetValue(key, (void *)value) ? 0 : EINVAL;
}
static inline void *pthread_getspecific(pthread_key_t key)
{
return TlsGetValue(key);
}
#endif /* PTHREAD_H */ #endif /* PTHREAD_H */

View File

@ -200,6 +200,7 @@ extern char *gitbasename(char *);
#include "compat/bswap.h" #include "compat/bswap.h"
/* General helper functions */ /* General helper functions */
extern void vreportf(const char *prefix, const char *err, va_list params);
extern NORETURN void usage(const char *err); extern NORETURN void usage(const char *err);
extern NORETURN void usagef(const char *err, ...) __attribute__((format (printf, 1, 2))); extern NORETURN void usagef(const char *err, ...) __attribute__((format (printf, 1, 2)));
extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2))); extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));

View File

@ -488,14 +488,12 @@ static NORETURN void die_webcgi(const char *err, va_list params)
static int dead; static int dead;
if (!dead) { if (!dead) {
char buffer[1000];
dead = 1; dead = 1;
vsnprintf(buffer, sizeof(buffer), err, params);
fprintf(stderr, "fatal: %s\n", buffer);
http_status(500, "Internal Server Error"); http_status(500, "Internal Server Error");
hdr_nocache(); hdr_nocache();
end_headers(); end_headers();
vreportf("fatal: ", err, params);
} }
exit(0); /* we successfully reported a failure ;-) */ exit(0); /* we successfully reported a failure ;-) */
} }

View File

@ -84,6 +84,7 @@ static NORETURN void die_child(const char *err, va_list params)
unused = write(child_err, "\n", 1); unused = write(child_err, "\n", 1);
exit(128); exit(128);
} }
#endif
static inline void set_cloexec(int fd) static inline void set_cloexec(int fd)
{ {
@ -91,7 +92,6 @@ static inline void set_cloexec(int fd)
if (flags >= 0) if (flags >= 0)
fcntl(fd, F_SETFD, flags | FD_CLOEXEC); fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
} }
#endif
static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure) static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure)
{ {
@ -449,11 +449,35 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
return run_command(&cmd); return run_command(&cmd);
} }
#ifdef WIN32 #ifndef NO_PTHREADS
static unsigned __stdcall run_thread(void *data) static pthread_t main_thread;
static int main_thread_set;
static pthread_key_t async_key;
static void *run_thread(void *data)
{ {
struct async *async = data; struct async *async = data;
return async->proc(async->proc_in, async->proc_out, async->data); intptr_t ret;
pthread_setspecific(async_key, async);
ret = async->proc(async->proc_in, async->proc_out, async->data);
return (void *)ret;
}
static NORETURN void die_async(const char *err, va_list params)
{
vreportf("fatal: ", err, params);
if (!pthread_equal(main_thread, pthread_self())) {
struct async *async = pthread_getspecific(async_key);
if (async->proc_in >= 0)
close(async->proc_in);
if (async->proc_out >= 0)
close(async->proc_out);
pthread_exit((void *)128);
}
exit(128);
} }
#endif #endif
@ -499,7 +523,7 @@ int start_async(struct async *async)
else else
proc_out = -1; proc_out = -1;
#ifndef WIN32 #ifdef NO_PTHREADS
/* Flush stdio before fork() to avoid cloning buffers */ /* Flush stdio before fork() to avoid cloning buffers */
fflush(NULL); fflush(NULL);
@ -526,12 +550,29 @@ int start_async(struct async *async)
else if (async->out) else if (async->out)
close(async->out); close(async->out);
#else #else
if (!main_thread_set) {
/*
* We assume that the first time that start_async is called
* it is from the main thread.
*/
main_thread_set = 1;
main_thread = pthread_self();
pthread_key_create(&async_key, NULL);
set_die_routine(die_async);
}
if (proc_in >= 0)
set_cloexec(proc_in);
if (proc_out >= 0)
set_cloexec(proc_out);
async->proc_in = proc_in; async->proc_in = proc_in;
async->proc_out = proc_out; async->proc_out = proc_out;
async->tid = (HANDLE) _beginthreadex(NULL, 0, run_thread, async, 0, NULL); {
if (!async->tid) { int err = pthread_create(&async->tid, NULL, run_thread, async);
error("cannot create thread: %s", strerror(errno)); if (err) {
goto error; error("cannot create thread: %s", strerror(err));
goto error;
}
} }
#endif #endif
return 0; return 0;
@ -551,17 +592,15 @@ error:
int finish_async(struct async *async) int finish_async(struct async *async)
{ {
#ifndef WIN32 #ifdef NO_PTHREADS
int ret = wait_or_whine(async->pid, "child process", 0); return wait_or_whine(async->pid, "child process", 0);
#else #else
DWORD ret = 0; void *ret = (void *)(intptr_t)(-1);
if (WaitForSingleObject(async->tid, INFINITE) != WAIT_OBJECT_0)
ret = error("waiting for thread failed: %lu", GetLastError()); if (pthread_join(async->tid, &ret))
else if (!GetExitCodeThread(async->tid, &ret)) error("pthread_join failed");
ret = error("cannot get thread exit code: %lu", GetLastError()); return (int)(intptr_t)ret;
CloseHandle(async->tid);
#endif #endif
return ret;
} }
int run_hook(const char *index_file, const char *name, ...) int run_hook(const char *index_file, const char *name, ...)

View File

@ -1,6 +1,10 @@
#ifndef RUN_COMMAND_H #ifndef RUN_COMMAND_H
#define RUN_COMMAND_H #define RUN_COMMAND_H
#ifndef NO_PTHREADS
#include <pthread.h>
#endif
struct child_process { struct child_process {
const char **argv; const char **argv;
pid_t pid; pid_t pid;
@ -74,10 +78,10 @@ struct async {
void *data; void *data;
int in; /* caller writes here and closes it */ int in; /* caller writes here and closes it */
int out; /* caller reads from here and closes it */ int out; /* caller reads from here and closes it */
#ifndef WIN32 #ifdef NO_PTHREADS
pid_t pid; pid_t pid;
#else #else
HANDLE tid; pthread_t tid;
int proc_in; int proc_in;
int proc_out; int proc_out;
#endif #endif

View File

@ -32,9 +32,9 @@ test_expect_success 'fsck fails' '
test_expect_success 'upload-pack fails due to error in pack-objects packing' ' test_expect_success 'upload-pack fails due to error in pack-objects packing' '
! echo "0032want $(git rev-parse HEAD) printf "0032want %s\n00000009done\n0000" \
00000009done $(git rev-parse HEAD) >input &&
0000" | git upload-pack . > /dev/null 2> output.err && test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
grep "unable to read" output.err && grep "unable to read" output.err &&
grep "pack-objects died" output.err grep "pack-objects died" output.err
' '
@ -51,9 +51,9 @@ test_expect_success 'fsck fails' '
' '
test_expect_success 'upload-pack fails due to error in rev-list' ' test_expect_success 'upload-pack fails due to error in rev-list' '
! echo "0032want $(git rev-parse HEAD) printf "0032want %s\n0034shallow %s00000009done\n0000" \
0034shallow $(git rev-parse HEAD^)00000009done $(git rev-parse HEAD) $(git rev-parse HEAD^) >input &&
0000" | git upload-pack . > /dev/null 2> output.err && test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
# pack-objects survived # pack-objects survived
grep "Total.*, reused" output.err && grep "Total.*, reused" output.err &&
# but there was an error, which must have been in rev-list # but there was an error, which must have been in rev-list
@ -62,9 +62,9 @@ test_expect_success 'upload-pack fails due to error in rev-list' '
test_expect_success 'upload-pack fails due to error in pack-objects enumeration' ' test_expect_success 'upload-pack fails due to error in pack-objects enumeration' '
! echo "0032want $(git rev-parse HEAD) printf "0032want %s\n00000009done\n0000" \
00000009done $(git rev-parse HEAD) >input &&
0000" | git upload-pack . > /dev/null 2> output.err && test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
grep "bad tree object" output.err && grep "bad tree object" output.err &&
grep "pack-objects died" output.err grep "pack-objects died" output.err
' '

10
usage.c
View File

@ -5,7 +5,7 @@
*/ */
#include "git-compat-util.h" #include "git-compat-util.h"
static void report(const char *prefix, const char *err, va_list params) void vreportf(const char *prefix, const char *err, va_list params)
{ {
char msg[4096]; char msg[4096];
vsnprintf(msg, sizeof(msg), err, params); vsnprintf(msg, sizeof(msg), err, params);
@ -14,24 +14,24 @@ static void report(const char *prefix, const char *err, va_list params)
static NORETURN void usage_builtin(const char *err, va_list params) static NORETURN void usage_builtin(const char *err, va_list params)
{ {
report("usage: ", err, params); vreportf("usage: ", err, params);
exit(129); exit(129);
} }
static NORETURN void die_builtin(const char *err, va_list params) static NORETURN void die_builtin(const char *err, va_list params)
{ {
report("fatal: ", err, params); vreportf("fatal: ", err, params);
exit(128); exit(128);
} }
static void error_builtin(const char *err, va_list params) static void error_builtin(const char *err, va_list params)
{ {
report("error: ", err, params); vreportf("error: ", err, params);
} }
static void warn_builtin(const char *warn, va_list params) static void warn_builtin(const char *warn, va_list params)
{ {
report("warning: ", warn, params); vreportf("warning: ", warn, params);
} }
/* If we are in a dlopen()ed .so write to a global variable would segfault /* If we are in a dlopen()ed .so write to a global variable would segfault