Code

Merge branch 'js/async-thread'
authorJunio C Hamano <gitster@pobox.com>
Mon, 21 Jun 2010 13:02:45 +0000 (06:02 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 21 Jun 2010 13:02:45 +0000 (06:02 -0700)
* 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

1  2 
compat/mingw.h
compat/win32/pthread.h
git-compat-util.h
http-backend.c
run-command.c

diff --combined compat/mingw.h
index f465566b7a5b8c852cad9b41e853aa97ba673b61,3347362632f009b973690300cfa039c36ab407f0..3b2477be5f658be665f19a12b48cc47fa07d1c6b
@@@ -80,7 -80,7 +80,7 @@@ static inline int fork(void
  static inline unsigned int alarm(unsigned int seconds)
  { return 0; }
  static inline int fsync(int fd)
 -{ return 0; }
 +{ return _commit(fd); }
  static inline int getppid(void)
  { return 1; }
  static inline void sync(void)
@@@ -89,7 -89,7 +89,7 @@@ static inline int getuid(
  { return 1; }
  static inline struct passwd *getpwnam(const char *name)
  { 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)
                return 0;
@@@ -170,9 -170,6 +170,9 @@@ int link(const char *oldpath, const cha
  int mingw_open (const char *filename, int oflags, ...);
  #define open mingw_open
  
 +ssize_t mingw_write(int fd, const void *buf, size_t count);
 +#define write mingw_write
 +
  FILE *mingw_fopen (const char *filename, const char *otype);
  #define fopen mingw_fopen
  
@@@ -232,7 -229,6 +232,7 @@@ int mingw_utime(const char *file_name, 
  #define utime mingw_utime
  
  pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
 +                   const char *dir,
                     int fhin, int fhout, int fherr);
  void mingw_execvp(const char *cmd, char *const *argv);
  #define execvp mingw_execvp
diff --combined compat/win32/pthread.h
index a45f8d66df8d1e452d9392945bf12a74c32bbca9,c7b8241b794705a337191add62556c9a9f268cd1..2e205485570bf62a11112c665624203207c724a9
   */
  #define pthread_mutex_t CRITICAL_SECTION
  
 -#define pthread_mutex_init(a,b) InitializeCriticalSection((a))
 +#define pthread_mutex_init(a,b) (InitializeCriticalSection((a)), 0)
  #define pthread_mutex_destroy(a) DeleteCriticalSection((a))
  #define pthread_mutex_lock EnterCriticalSection
  #define pthread_mutex_unlock LeaveCriticalSection
  
 +typedef int pthread_mutexattr_t;
 +#define pthread_mutexattr_init(a) (*(a) = 0)
 +#define pthread_mutexattr_destroy(a) do {} while (0)
 +#define pthread_mutexattr_settype(a, t) 0
 +#define PTHREAD_MUTEX_RECURSIVE 0
 +
  /*
   * Implement simple condition variable for Windows threads, based on ACE
   * implementation.
@@@ -58,6 -52,7 +58,7 @@@ typedef struct 
        HANDLE handle;
        void *(*start_routine)(void*);
        void *arg;
+       DWORD tid;
  } pthread_t;
  
  extern int pthread_create(pthread_t *thread, const void *unused,
  
  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 */
diff --combined git-compat-util.h
index c9d53397ad6212cd1a10ab6c9e34d1b537ec486a,3cabcdd8c40c34de25a59772fcd72a5c45dc65cc..02a73eeb667e798fca29de25fed6b0b2900f6912
@@@ -55,8 -55,7 +55,8 @@@
  # else
  # define _XOPEN_SOURCE 500
  # endif
 -#elif !defined(__APPLE__) && !defined(__FreeBSD__)  && !defined(__USLC__) && !defined(_M_UNIX) && !defined(sgi)
 +#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
 +      !defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__)
  #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
  #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
  #endif
@@@ -164,13 -163,6 +164,13 @@@ extern char *gitbasename(char *)
  #define PATH_SEP ':'
  #endif
  
 +#ifdef HAVE_PATHS_H
 +#include <paths.h>
 +#endif
 +#ifndef _PATH_DEFPATH
 +#define _PATH_DEFPATH "/usr/local/bin:/usr/bin:/bin"
 +#endif
 +
  #ifndef STRIP_EXTENSION
  #define STRIP_EXTENSION ""
  #endif
  #include "compat/bswap.h"
  
  /* 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 usagef(const char *err, ...) __attribute__((format (printf, 1, 2)));
  extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
@@@ -224,6 -217,7 +225,6 @@@ static inline const char *skip_prefix(c
  #define PROT_READ 1
  #define PROT_WRITE 2
  #define MAP_PRIVATE 1
 -#define MAP_FAILED ((void*)-1)
  #endif
  
  #define mmap git_mmap
@@@ -252,10 -246,6 +253,10 @@@ extern int git_munmap(void *start, size
  
  #endif /* NO_MMAP */
  
 +#ifndef MAP_FAILED
 +#define MAP_FAILED ((void *)-1)
 +#endif
 +
  #ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
  #define on_disk_bytes(st) ((st).st_size)
  #else
@@@ -342,7 -332,6 +343,7 @@@ extern int git_vsnprintf(char *str, siz
  #ifdef __GLIBC_PREREQ
  #if __GLIBC_PREREQ(2, 1)
  #define HAVE_STRCHRNUL
 +#define HAVE_MEMPCPY
  #endif
  #endif
  
@@@ -356,19 -345,8 +357,19 @@@ static inline char *gitstrchrnul(const 
  }
  #endif
  
 +#ifndef HAVE_MEMPCPY
 +#define mempcpy gitmempcpy
 +static inline void *gitmempcpy(void *dest, const void *src, size_t n)
 +{
 +      return (char *)memcpy(dest, src, n) + n;
 +}
 +#endif
 +
  extern void release_pack_memory(size_t, int);
  
 +typedef void (*try_to_free_t)(size_t);
 +extern try_to_free_t set_try_to_free_routine(try_to_free_t);
 +
  extern char *xstrdup(const char *str);
  extern void *xmalloc(size_t size);
  extern void *xmallocz(size_t size);
@@@ -492,14 -470,5 +493,14 @@@ void git_qsort(void *base, size_t nmemb
   * Always returns the return value of unlink(2).
   */
  int unlink_or_warn(const char *path);
 +/*
 + * Likewise for rmdir(2).
 + */
 +int rmdir_or_warn(const char *path);
 +/*
 + * Calls the correct function out of {unlink,rmdir}_or_warn based on
 + * the supplied file mode.
 + */
 +int remove_or_warn(unsigned int mode, const char *path);
  
  #endif
diff --combined http-backend.c
index f0e787e37d9384c11d48dbbc35cfb2b1609368e8,8c7b7d09eaae0d5ac1f3d00f5e3bf9ceb92b54de..44ce6bb32b0e98681f7c523537082065e8cb5fc1
@@@ -6,7 -6,6 +6,7 @@@
  #include "exec_cmd.h"
  #include "run-command.h"
  #include "string-list.h"
 +#include "url.h"
  
  static const char content_type[] = "Content-Type";
  static const char content_length[] = "Content-Length";
@@@ -26,6 -25,60 +26,6 @@@ static struct rpc_service rpc_service[
        { "receive-pack", "receivepack", -1 },
  };
  
 -static int decode_char(const char *q)
 -{
 -      int i;
 -      unsigned char val = 0;
 -      for (i = 0; i < 2; i++) {
 -              unsigned char c = *q++;
 -              val <<= 4;
 -              if (c >= '0' && c <= '9')
 -                      val += c - '0';
 -              else if (c >= 'a' && c <= 'f')
 -                      val += c - 'a' + 10;
 -              else if (c >= 'A' && c <= 'F')
 -                      val += c - 'A' + 10;
 -              else
 -                      return -1;
 -      }
 -      return val;
 -}
 -
 -static char *decode_parameter(const char **query, int is_name)
 -{
 -      const char *q = *query;
 -      struct strbuf out;
 -
 -      strbuf_init(&out, 16);
 -      do {
 -              unsigned char c = *q;
 -
 -              if (!c)
 -                      break;
 -              if (c == '&' || (is_name && c == '=')) {
 -                      q++;
 -                      break;
 -              }
 -
 -              if (c == '%') {
 -                      int val = decode_char(q + 1);
 -                      if (0 <= val) {
 -                              strbuf_addch(&out, val);
 -                              q += 3;
 -                              continue;
 -                      }
 -              }
 -
 -              if (c == '+')
 -                      strbuf_addch(&out, ' ');
 -              else
 -                      strbuf_addch(&out, c);
 -              q++;
 -      } while (1);
 -      *query = q;
 -      return strbuf_detach(&out, NULL);
 -}
 -
  static struct string_list *get_parameters(void)
  {
        if (!query_params) {
@@@ -33,8 -86,8 +33,8 @@@
  
                query_params = xcalloc(1, sizeof(*query_params));
                while (query && *query) {
 -                      char *name = decode_parameter(&query, 1);
 -                      char *value = decode_parameter(&query, 0);
 +                      char *name = url_decode_parameter_name(&query);
 +                      char *value = url_decode_parameter_value(&query);
                        struct string_list_item *i;
  
                        i = string_list_lookup(name, query_params);
@@@ -485,19 -538,12 +485,17 @@@ static void service_rpc(char *service_n
  
  static NORETURN void die_webcgi(const char *err, va_list params)
  {
 -      http_status(500, "Internal Server Error");
 -      hdr_nocache();
 -      end_headers();
 +      static int dead;
  
 -      vreportf("fatal: ", err, params);
 -      exit(0);
 +      if (!dead) {
-               char buffer[1000];
 +              dead = 1;
-               vsnprintf(buffer, sizeof(buffer), err, params);
-               fprintf(stderr, "fatal: %s\n", buffer);
 +              http_status(500, "Internal Server Error");
 +              hdr_nocache();
 +              end_headers();
++
++              vreportf("fatal: ", err, params);
 +      }
 +      exit(0); /* we successfully reported a failure ;-) */
  }
  
  static char* getdir(void)
diff --combined run-command.c
index c7793f50fbe0a43495c2b2d36a47c0b5aac37483,61b153987b0efb9d9c5c53e2a4aa6893a9362f01..2a1041ef6599c84fff6a8d9faf5dea23a2af3ab0
@@@ -67,23 -67,22 +67,24 @@@ static int child_notifier = -1
  
  static void notify_parent(void)
  {
 -      write(child_notifier, "", 1);
 +      ssize_t unused;
 +      unused = write(child_notifier, "", 1);
  }
  
  static NORETURN void die_child(const char *err, va_list params)
  {
        char msg[4096];
 +      ssize_t unused;
        int len = vsnprintf(msg, sizeof(msg), err, params);
        if (len > sizeof(msg))
                len = sizeof(msg);
  
 -      write(child_err, "fatal: ", 7);
 -      write(child_err, msg, len);
 -      write(child_err, "\n", 1);
 +      unused = write(child_err, "fatal: ", 7);
 +      unused = write(child_err, msg, len);
 +      unused = write(child_err, "\n", 1);
        exit(128);
  }
+ #endif
  
  static inline void set_cloexec(int fd)
  {
@@@ -91,7 -90,6 +92,6 @@@
        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)
  {
@@@ -342,6 -340,8 +342,6 @@@ fail_pipe
        else if (cmd->out > 1)
                fhout = dup(cmd->out);
  
 -      if (cmd->dir)
 -              die("chdir in start_command() not implemented");
        if (cmd->env)
                env = make_augmented_environ(cmd->env);
  
                cmd->argv = prepare_shell_cmd(cmd->argv);
        }
  
 -      cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env,
 +      cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env, cmd->dir,
                                  fhin, fhout, fherr);
        failed_errno = errno;
        if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
                        close(cmd->out);
                if (need_err)
                        close_pair(fderr);
 +              else if (cmd->err)
 +                      close(cmd->err);
                errno = failed_errno;
                return -1;
        }
@@@ -449,11 -447,35 +449,35 @@@ int run_command_v_opt_cd_env(const cha
        return run_command(&cmd);
  }
  
- #ifdef WIN32
- static unsigned __stdcall run_thread(void *data)
+ #ifndef NO_PTHREADS
+ 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;
-       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
  
@@@ -499,7 -521,7 +523,7 @@@ int start_async(struct async *async
        else
                proc_out = -1;
  
- #ifndef WIN32
+ #ifdef NO_PTHREADS
        /* Flush stdio before fork() to avoid cloning buffers */
        fflush(NULL);
  
        else if (async->out)
                close(async->out);
  #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_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;
@@@ -551,17 -590,15 +592,15 @@@ error
  
  int finish_async(struct async *async)
  {
- #ifndef WIN32
-       int ret = wait_or_whine(async->pid, "child process", 0);
+ #ifdef NO_PTHREADS
+       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, ...)