[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

[PATCH] Multi-threaded svnserve

From: Philip Martin <philip_at_codematters.co.uk>
Date: 2003-06-02 04:05:43 CEST

Hello

Here's a patch to add a multi-thread capability to svnserve. On Linux
this multithreaded svnserve will pass the regression tests (with the
exception of basic_tests 11, but that fails even without this patch).
The big advantage of a multi-threaded svnserve is that the operations
that require two simultaneous connections should work even if fork is
not available. Could someone with a Windows box give it a spin? [I'm
assuming svnserve already builds on Windows, but that it operates "one
connection at a time".]

Note: this patch enables threading even if fork is available, that's
so that I can test it on Linux.

Index: subversion/svnserve/main.c
===================================================================
--- subversion/svnserve/main.c (revision 6108)
+++ subversion/svnserve/main.c (working copy)
@@ -26,6 +26,7 @@
 #include <apr_getopt.h>
 #include <apr_network_io.h>
 #include <apr_signal.h>
+#include <apr_thread_proc.h>
 
 #include "svn_types.h"
 #include "svn_string.h"
@@ -37,6 +38,15 @@
 
 #include "server.h"
 
+#if APR_HAS_FORK
+/* ### for testing threading is enabled even when fork is available */
+#define SVNSERVE_FORK 0
+#define SVNSERVE_THREAD 1
+#elif APR_HAS_THREADS
+#define SVNSERVE_FORK 0
+#define SVNSERVE_THREAD 1
+#endif
+
 static void usage(const char *progname)
 {
   if (!progname)
@@ -45,10 +55,12 @@
   exit(1);
 }
 
+#if SVNSERVE_FORK
 static void sigchld_handler(int signo)
 {
   /* Nothing to do; we just need to interrupt the accept(). */
 }
+#endif
 
 /* In tunnel or inetd mode, we don't want hook scripts corrupting the
  * data stream by sending data to stdout, so we need to redirect
@@ -66,6 +78,27 @@
   return apr_file_dup2(out_file, err_file, pool);
 }
 
+#if SVNSERVE_THREAD
+struct serve_thread_t {
+ const char *root;
+ svn_ra_svn_conn_t *conn;
+ svn_boolean_t read_only;
+ apr_pool_t *pool;
+};
+
+static void * APR_THREAD_FUNC
+serve_thread(apr_thread_t *tid,
+ void *data)
+{
+ struct serve_thread_t *d = data;
+
+ svn_error_clear(serve(d->conn, d->root, FALSE, d->read_only, d->pool));
+ svn_pool_destroy(d->pool);
+
+ return NULL;
+}
+#endif
+
 int main(int argc, const char *const *argv)
 {
   svn_boolean_t listen_once = FALSE, daemon_mode = FALSE, tunnel_mode = FALSE;
@@ -81,8 +114,12 @@
   const char *arg, *root = "/";
   apr_status_t status;
   svn_ra_svn_conn_t *conn;
-#if APR_HAS_FORK
+#if SVNSERVE_FORK
   apr_proc_t proc;
+#elif SVNSERVE_THREAD
+ apr_threadattr_t *tattr;
+ apr_thread_t *tid;
+ struct serve_thread_t *thread_data;
 #endif
 
   /* Initialize the app. */
@@ -163,28 +200,43 @@
 
   apr_listen(sock, 7);
 
-#if APR_HAS_FORK
+#if SVNSERVE_FORK || SVNSERVE_THREAD
   if (!listen_once)
     apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
+#endif
 
+#if SVNSERVE_FORK
   apr_signal(SIGCHLD, sigchld_handler);
 #endif
 
+#if ! SVNSERVE_THREAD
   connection_pool = svn_pool_create(pool);
+#endif
+
   while (1)
     {
+#if SVNSERVE_THREAD
+ connection_pool = svn_pool_create(NULL);
+ thread_data = apr_palloc(connection_pool, sizeof(*thread_data));
+#else
       /* Clear the pool for each iteration. */
       apr_pool_clear(connection_pool);
+#endif
 
       status = apr_accept(&usock, sock, connection_pool);
-#if APR_HAS_FORK
+#if SVNSERVE_FORK
       /* Collect any zombie child processes. */
       while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT,
                                      connection_pool) == APR_CHILD_DONE)
         ;
 #endif
       if (APR_STATUS_IS_EINTR(status))
- continue;
+ {
+#if SVNSERVE_THREAD
+ svn_pool_destroy(connection_pool);
+#endif
+ continue;
+ }
       if (status)
         {
           fprintf(stderr, "Can't accept client connection: %s\n",
@@ -207,9 +259,7 @@
           exit(0);
         }
 
- /* ### We should try to use threads when APR_HAS_FORK isn't
- * defined (such as on Windows). */
-#if APR_HAS_FORK
+#if SVNSERVE_FORK
       status = apr_proc_fork(&proc, connection_pool);
       if (status == APR_INCHILD)
         {
@@ -227,6 +277,36 @@
           /* Log an error, when we support logging. */
           apr_socket_close(usock);
         }
+#elif SVNSERVE_THREAD
+ /* Create a detached thread for each connection. That's not a
+ particularly sophisticated strategy for a threaded server, it's
+ little different from forking one process per connection. */
+ status = apr_threadattr_create(&tattr, connection_pool);
+ if (status)
+ {
+ fprintf(stderr, "Can't create threadattr: %s\n",
+ apr_strerror(status, errbuf, sizeof(errbuf)));
+ exit(1);
+ }
+ status = apr_threadattr_detach_set(tattr, 1);
+ if (status)
+ {
+ fprintf(stderr, "Can't set detached state: %s\n",
+ apr_strerror(status, errbuf, sizeof(errbuf)));
+ exit(1);
+ }
+ thread_data->conn = conn;
+ thread_data->root = root;
+ thread_data->read_only = read_only;
+ thread_data->pool = connection_pool;
+ status = apr_thread_create(&tid, tattr, serve_thread, thread_data,
+ connection_pool);
+ if (status)
+ {
+ fprintf(stderr, "Can't create thread: %s\n",
+ apr_strerror(status, errbuf, sizeof(errbuf)));
+ exit(1);
+ }
 #else
       /* Serve one connection at a time. */
       svn_error_clear(serve(conn, root, FALSE, read_only, connection_pool));

-- 
Philip Martin
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Mon Jun 2 04:06:30 2003

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.