You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by bl...@apache.org on 2011/01/27 02:22:28 UTC

svn commit: r1063946 - /subversion/trunk/subversion/libsvn_subr/io.c

Author: blair
Date: Thu Jan 27 01:22:28 2011
New Revision: 1063946

URL: http://svn.apache.org/viewvc?rev=1063946&view=rev
Log:
Fix a deadlock that can occur when two or more multithreaded
Subversion servers on the same system serve two or more fsfs
repositories.  See the comment in svn_io_file_lock2() and/or this
thread:

http://mail-archives.apache.org/mod_mbox/subversion-dev/201101.mbox/%3C4D3FC68F.7020709@orcaware.com%3E

* subversion/libsvn_subr/io.c
  (WIN32_RETRY_LOOP):
    Move the body of this macro into and use...
  (RETRY_LOOP):
    ...this new macro which abstracts out the test on when to retry.
  (EDEADLK_RETRY_LOOP):
    New macro that retries if getting a lock returned EDEADLK.  This
    is only defined if EDEADLK is defined in a multithread capable
    system, as deadlocks won't occur in single-threaded processes.
  (svn_io_file_lock2):
    Use EDEADLK_RETRY_LOOP on apr_file_lock().

Modified:
    subversion/trunk/subversion/libsvn_subr/io.c

Modified: subversion/trunk/subversion/libsvn_subr/io.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/io.c?rev=1063946&r1=1063945&r2=1063946&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/io.c (original)
+++ subversion/trunk/subversion/libsvn_subr/io.c Thu Jan 27 01:22:28 2011
@@ -85,19 +85,14 @@
 #define RETRY_INITIAL_SLEEP 1000
 #define RETRY_MAX_SLEEP 128000
 
-#ifndef WIN32_RETRY_LOOP
-#if defined(WIN32) && !defined(SVN_NO_WIN32_RETRY_LOOP)
-#define WIN32_RETRY_LOOP(err, expr)                                        \
+#define RETRY_LOOP(err, expr, test)                                        \
   do                                                                       \
     {                                                                      \
       apr_status_t os_err = APR_TO_OS_ERROR(err);                          \
       int sleep_count = RETRY_INITIAL_SLEEP;                               \
       int retries;                                                         \
       for (retries = 0;                                                    \
-           retries < RETRY_MAX_ATTEMPTS &&                                 \
-           (os_err == ERROR_ACCESS_DENIED                                  \
-            || os_err == ERROR_SHARING_VIOLATION                           \
-            || os_err == ERROR_DIR_NOT_EMPTY);                             \
+           retries < RETRY_MAX_ATTEMPTS && (test);                         \
            ++retries, os_err = APR_TO_OS_ERROR(err))                       \
         {                                                                  \
           apr_sleep(sleep_count);                                          \
@@ -107,6 +102,20 @@
         }                                                                  \
     }                                                                      \
   while (0)
+
+#if defined(EDEADLK) && APR_HAS_THREADS
+#define EDEADLK_RETRY_LOOP(err, expr)                                      \
+  RETRY_LOOP(err, expr, (os_err == EDEADLK))
+#else
+#define EDEADLK_RETRY_LOOP(err, expr) ((void)0)
+#endif
+
+#ifndef WIN32_RETRY_LOOP
+#if defined(WIN32) && !defined(SVN_NO_WIN32_RETRY_LOOP)
+#define WIN32_RETRY_LOOP(err, expr)                                        \
+  RETRY_LOOP(err, expr, (os_err == ERROR_ACCESS_DENIED                     \
+                         || os_err == ERROR_SHARING_VIOLATION              \
+                         || os_err == ERROR_DIR_NOT_EMPTY))
 #else
 #define WIN32_RETRY_LOOP(err, expr) ((void)0)
 #endif
@@ -1695,6 +1704,22 @@ svn_io_file_lock2(const char *lock_file,
 
   /* Get lock on the filehandle. */
   apr_err = apr_file_lock(lockfile_handle, locktype);
+
+  /* In deployments with two or more multithreaded servers running on
+     the same system serving two or more fsfs repositories it is
+     possible for a deadlock to occur when getting a write lock on
+     db/txn-current-lock:
+
+     Process 1                         Process 2
+     ---------                         ---------
+     thread 1: get lock in repos A
+                                       thread 1: get lock in repos B
+                                       thread 2: block getting lock in repos A
+     thread 2: try to get lock in B *** deadlock ***
+
+     Retry for a while for the deadlock to clear. */
+  EDEADLK_RETRY_LOOP(apr_err, apr_file_lock(lockfile_handle, locktype));
+
   if (apr_err)
     {
       switch (locktype & APR_FLOCK_TYPEMASK)