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