You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2011/10/12 14:38:44 UTC

svn commit: r1182334 [2/5] - in /subversion/branches/showing-merge-info: ./ subversion/bindings/javahl/native/ subversion/bindings/javahl/src/org/tigris/subversion/javahl/ subversion/bindings/javahl/tests/org/apache/subversion/javahl/ subversion/includ...

Modified: subversion/branches/showing-merge-info/subversion/libsvn_subr/cache-inprocess.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_subr/cache-inprocess.c?rev=1182334&r1=1182333&r2=1182334&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_subr/cache-inprocess.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_subr/cache-inprocess.c Wed Oct 12 12:38:41 2011
@@ -30,6 +30,7 @@
 #include "svn_private_config.h"
 
 #include "cache.h"
+#include "private/svn_mutex.h"
 
 /* The (internal) cache object. */
 typedef struct inprocess_cache_t {
@@ -80,12 +81,10 @@ typedef struct inprocess_cache_t {
    */
   apr_size_t data_size;
 
-#if APR_HAS_THREADS
   /* A lock for intra-process synchronization to the cache, or NULL if
    * the cache's creator doesn't feel the cache needs to be
    * thread-safe. */
-  apr_thread_mutex_t *mutex;
-#endif
+  svn_mutex__t *mutex;
 } inprocess_cache_t;
 
 /* A cache page; all items on the page are allocated from the same
@@ -182,39 +181,28 @@ duplicate_key(inprocess_cache_t *cache,
     return apr_pmemdup(pool, key, cache->klen);
 }
 
-/* If applicable, locks CACHE's mutex. */
 static svn_error_t *
-lock_cache(inprocess_cache_t *cache)
+inprocess_cache_get_internal(char **buffer,
+                             apr_size_t *size,
+                             inprocess_cache_t *cache,
+                             const void *key,
+                             apr_pool_t *result_pool)
 {
-#if APR_HAS_THREADS
-  apr_status_t status;
-  if (! cache->mutex)
-    return SVN_NO_ERROR;
-
-  status = apr_thread_mutex_lock(cache->mutex);
-  if (status)
-    return svn_error_wrap_apr(status, _("Can't lock cache mutex"));
-#endif
+  struct cache_entry *entry = apr_hash_get(cache->hash, key, cache->klen);
 
-  return SVN_NO_ERROR;
-}
-
-/* If applicable, unlocks CACHE's mutex, then returns ERR. */
-static svn_error_t *
-unlock_cache(inprocess_cache_t *cache,
-             svn_error_t *err)
-{
-#if APR_HAS_THREADS
-  apr_status_t status;
-  if (! cache->mutex)
-    return err;
+  *buffer = NULL;
+  if (entry)
+    {
+      SVN_ERR(move_page_to_front(cache, entry->page));
 
-  status = apr_thread_mutex_unlock(cache->mutex);
-  if (status && !err)
-    return svn_error_wrap_apr(status, _("Can't unlock cache mutex"));
-#endif
+      /* duplicate the buffer entry */
+      *buffer = apr_palloc(result_pool, entry->size);
+      memcpy(*buffer, entry->value, entry->size);
 
-  return err;
+      *size = entry->size;
+    }
+    
+  return SVN_NO_ERROR;
 }
 
 static svn_error_t *
@@ -225,38 +213,24 @@ inprocess_cache_get(void **value_p,
                     apr_pool_t *result_pool)
 {
   inprocess_cache_t *cache = cache_void;
-  svn_error_t *err = NULL;
-  struct cache_entry *entry;
   char* buffer;
+  apr_size_t size;
 
-  SVN_ERR(lock_cache(cache));
-
-  entry = apr_hash_get(cache->hash, key, cache->klen);
-  if (! entry)
-    {
-      *found = FALSE;
-      return unlock_cache(cache, SVN_NO_ERROR);
-    }
-
-  SVN_ERR(move_page_to_front(cache, entry->page));
-
-  /* duplicate the buffer entry */
-  buffer = apr_palloc(result_pool, entry->size);
-  memcpy(buffer, entry->value, entry->size);
-
-  /* the cache is no longer being accessed */
-  SVN_ERR(unlock_cache(cache, SVN_NO_ERROR));
-
+  SVN_MUTEX__WITH_LOCK(cache->mutex,
+                       inprocess_cache_get_internal(&buffer,
+                                                    &size,
+                                                    cache,
+                                                    key,
+                                                    result_pool));
+    
   /* deserialize the buffer content. Usually, this will directly
      modify the buffer content directly.
    */
-  *found = TRUE;
-  if (entry->value)
-    err = cache->deserialize_func(value_p, buffer, entry->size, result_pool);
-  else
-    *value_p = NULL;
-
-  return err;
+  *value_p = NULL;
+  *found = buffer != NULL;
+  return buffer && size
+    ? cache->deserialize_func(value_p, buffer, size, result_pool)
+    : SVN_NO_ERROR;
 }
 
 /* Removes PAGE from the LRU list, removes all of its entries from
@@ -292,16 +266,12 @@ erase_page(inprocess_cache_t *cache,
 
 
 static svn_error_t *
-inprocess_cache_set(void *cache_void,
-                    const void *key,
-                    void *value,
-                    apr_pool_t *scratch_pool)
+inprocess_cache_set_internal(inprocess_cache_t *cache,
+                             const void *key,
+                             void *value,
+                             apr_pool_t *scratch_pool)
 {
-  inprocess_cache_t *cache = cache_void;
   struct cache_entry *existing_entry;
-  svn_error_t *err = SVN_NO_ERROR;
-
-  SVN_ERR(lock_cache(cache));
 
   existing_entry = apr_hash_get(cache->hash, key, cache->klen);
 
@@ -333,11 +303,13 @@ inprocess_cache_set(void *cache_void,
       cache->data_size -= existing_entry->size;
       if (value)
         {
-          err = cache->serialize_func((char **)&existing_entry->value,
-                                      &existing_entry->size,
-                                      value,
-                                      page->page_pool);
+          SVN_ERR(cache->serialize_func((char **)&existing_entry->value,
+                                        &existing_entry->size,
+                                        value,
+                                        page->page_pool));
           cache->data_size += existing_entry->size;
+          if (existing_entry->size == 0)
+            existing_entry->value = NULL;
         }
       else
         {
@@ -345,7 +317,7 @@ inprocess_cache_set(void *cache_void,
           existing_entry->size = 0;
         }
 
-      goto cleanup;
+      return SVN_NO_ERROR;
     }
 
   /* Do we not have a partial page to put it on, but we are allowed to
@@ -383,11 +355,13 @@ inprocess_cache_set(void *cache_void,
     new_entry->key = duplicate_key(cache, key, page->page_pool);
     if (value)
       {
-        err = cache->serialize_func((char **)&new_entry->value,
-                                    &new_entry->size,
-                                    value,
-                                    page->page_pool);
+        SVN_ERR(cache->serialize_func((char **)&new_entry->value,
+                                      &new_entry->size,
+                                      value,
+                                      page->page_pool));
         cache->data_size += new_entry->size;
+        if (new_entry->size == 0)
+          new_entry->value = NULL;
       }
     else
       {
@@ -395,9 +369,6 @@ inprocess_cache_set(void *cache_void,
         new_entry->size = 0;
       }
 
-    if (err)
-      goto cleanup;
-
     /* Add the entry to the page's list. */
     new_entry->page = page;
     new_entry->next_entry = page->first_entry;
@@ -418,8 +389,24 @@ inprocess_cache_set(void *cache_void,
       }
   }
 
- cleanup:
-  return unlock_cache(cache, err);
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+inprocess_cache_set(void *cache_void,
+                    const void *key,
+                    void *value,
+                    apr_pool_t *scratch_pool)
+{
+  inprocess_cache_t *cache = cache_void;
+
+  SVN_MUTEX__WITH_LOCK(cache->mutex,
+                       inprocess_cache_set_internal(cache,
+                                                    key,
+                                                    value,
+                                                    scratch_pool));
+
+  return SVN_NO_ERROR;
 }
 
 /* Baton type for svn_cache__iter. */
@@ -455,10 +442,33 @@ inprocess_cache_iter(svn_boolean_t *comp
   b.user_cb = user_cb;
   b.user_baton = user_baton;
 
-  SVN_ERR(lock_cache(cache));
-  return unlock_cache(cache,
-                      svn_iter_apr_hash(completed, cache->hash, iter_cb, &b,
-                                        scratch_pool));
+  SVN_MUTEX__WITH_LOCK(cache->mutex,
+                       svn_iter_apr_hash(completed, cache->hash, 
+                                         iter_cb, &b, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+inprocess_cache_get_partial_internal(void **value_p,
+                                     svn_boolean_t *found,
+                                     inprocess_cache_t *cache,
+                                     const void *key,
+                                     svn_cache__partial_getter_func_t func,
+                                     void *baton,
+                                     apr_pool_t *result_pool)
+{
+  struct cache_entry *entry = apr_hash_get(cache->hash, key, cache->klen);
+  if (! entry)
+    {
+      *found = FALSE;
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(move_page_to_front(cache, entry->page));
+
+  *found = TRUE;
+  return func(value_p, entry->value, entry->size, baton, result_pool);
 }
 
 static svn_error_t *
@@ -471,23 +481,40 @@ inprocess_cache_get_partial(void **value
                             apr_pool_t *result_pool)
 {
   inprocess_cache_t *cache = cache_void;
-  struct cache_entry *entry;
 
-  SVN_ERR(lock_cache(cache));
+  SVN_MUTEX__WITH_LOCK(cache->mutex,
+                       inprocess_cache_get_partial_internal(value_p,
+                                                            found,
+                                                            cache,
+                                                            key,
+                                                            func,
+                                                            baton,
+                                                            result_pool));
 
-  entry = apr_hash_get(cache->hash, key, cache->klen);
-  if (! entry)
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+inprocess_cache_set_partial_internal(inprocess_cache_t *cache,
+                                     const void *key,
+                                     svn_cache__partial_setter_func_t func,
+                                     void *baton,
+                                     apr_pool_t *scratch_pool)
+{
+  struct cache_entry *entry = apr_hash_get(cache->hash, key, cache->klen);
+  if (entry)
     {
-      *found = FALSE;
-      return unlock_cache(cache, SVN_NO_ERROR);
-    }
+      SVN_ERR(move_page_to_front(cache, entry->page));
 
-  SVN_ERR(move_page_to_front(cache, entry->page));
+      cache->data_size -= entry->size;
+      SVN_ERR(func((char **)&entry->value,
+                  &entry->size,
+                  baton,
+                  entry->page->page_pool));
+      cache->data_size += entry->size;
+    }
 
-  *found = TRUE;
-  return unlock_cache(cache,
-                      func(value_p, entry->value, entry->size, baton,
-                           result_pool));
+  return SVN_NO_ERROR;
 }
 
 static svn_error_t *
@@ -498,25 +525,15 @@ inprocess_cache_set_partial(void *cache_
                             apr_pool_t *scratch_pool)
 {
   inprocess_cache_t *cache = cache_void;
-  struct cache_entry *entry;
-  svn_error_t *err = SVN_NO_ERROR;
-
-  SVN_ERR(lock_cache(cache));
-
-  entry = apr_hash_get(cache->hash, key, cache->klen);
-  if (! entry)
-    return unlock_cache(cache, err);
 
-  SVN_ERR(move_page_to_front(cache, entry->page));
-
-  cache->data_size -= entry->size;
-  err = func((char **)&entry->value,
-             &entry->size,
-             baton,
-             entry->page->page_pool);
-  cache->data_size += entry->size;
+  SVN_MUTEX__WITH_LOCK(cache->mutex,
+                       inprocess_cache_set_partial_internal(cache,
+                                                            key,
+                                                            func,
+                                                            baton,
+                                                            scratch_pool));
 
-  return unlock_cache(cache, err);
+  return SVN_NO_ERROR;
 }
 
 static svn_boolean_t
@@ -532,15 +549,10 @@ inprocess_cache_is_cachable(void *cache_
 }
 
 static svn_error_t *
-inprocess_cache_get_info(void *cache_void,
-                         svn_cache__info_t *info,
-                         svn_boolean_t reset,
-                         apr_pool_t *result_pool)
+inprocess_cache_get_info_internal(inprocess_cache_t *cache,
+                                  svn_cache__info_t *info,
+                                  apr_pool_t *result_pool)
 {
-  inprocess_cache_t *cache = cache_void;
-
-  SVN_ERR(lock_cache(cache));
-
   info->id = apr_pstrdup(result_pool, cache->id);
 
   info->used_entries = apr_hash_count(cache->hash);
@@ -552,10 +564,26 @@ inprocess_cache_get_info(void *cache_voi
                    + cache->items_per_page * sizeof(struct cache_page)
                    + info->used_entries * sizeof(struct cache_entry);
 
-  return unlock_cache(cache, SVN_NO_ERROR);
+  return SVN_NO_ERROR;
 }
 
 
+static svn_error_t *
+inprocess_cache_get_info(void *cache_void,
+                         svn_cache__info_t *info,
+                         svn_boolean_t reset,
+                         apr_pool_t *result_pool)
+{
+  inprocess_cache_t *cache = cache_void;
+
+  SVN_MUTEX__WITH_LOCK(cache->mutex,
+                       inprocess_cache_get_info_internal(cache,
+                                                         info,
+                                                         result_pool));
+
+  return SVN_NO_ERROR;
+}
+
 static svn_cache__vtable_t inprocess_cache_vtable = {
   inprocess_cache_get,
   inprocess_cache_set,
@@ -602,17 +630,7 @@ svn_cache__create_inprocess(svn_cache__t
   /* The sentinel doesn't need a pool.  (We're happy to crash if we
    * accidentally try to treat it like a real page.) */
 
-#if APR_HAS_THREADS
-  if (thread_safe)
-    {
-      apr_status_t status = apr_thread_mutex_create(&(cache->mutex),
-                                                    APR_THREAD_MUTEX_DEFAULT,
-                                                    pool);
-      if (status)
-        return svn_error_wrap_apr(status,
-                                  _("Can't create cache mutex"));
-    }
-#endif
+  SVN_ERR(svn_mutex__init(&cache->mutex, thread_safe, pool));
 
   cache->cache_pool = pool;
 

Modified: subversion/branches/showing-merge-info/subversion/libsvn_subr/cache-membuffer.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_subr/cache-membuffer.c?rev=1182334&r1=1182333&r2=1182334&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_subr/cache-membuffer.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_subr/cache-membuffer.c Wed Oct 12 12:38:41 2011
@@ -30,6 +30,7 @@
 #include "cache.h"
 #include "svn_string.h"
 #include "private/svn_dep_compat.h"
+#include "private/svn_mutex.h"
 
 /*
  * This svn_cache__t implementation actually consists of two parts:
@@ -423,13 +424,11 @@ struct svn_membuffer_t
    */
   apr_uint64_t total_hits;
 
-#if APR_HAS_THREADS
   /* A lock for intra-process synchronization to the cache, or NULL if
    * the cache's creator doesn't feel the cache needs to be
    * thread-safe.
    */
-  apr_thread_mutex_t *mutex;
-#endif
+  svn_mutex__t *mutex;
 };
 
 /* Align integer VALUE to the next ITEM_ALIGNMENT boundary.
@@ -440,43 +439,6 @@ struct svn_membuffer_t
  */
 #define ALIGN_POINTER(pointer) ((void*)ALIGN_VALUE((apr_size_t)(char*)(pointer)))
 
-/* Acquire the cache mutex, if necessary.
- */
-static svn_error_t *
-lock_cache(svn_membuffer_t *cache)
-{
-#if APR_HAS_THREADS
-  if (cache->mutex)
-  {
-    apr_status_t status = apr_thread_mutex_lock(cache->mutex);
-    if (status)
-      return svn_error_wrap_apr(status, _("Can't lock cache mutex"));
-  }
-#endif
-
-  return SVN_NO_ERROR;
-}
-
-/* Release the cache mutex, if necessary.
- */
-static svn_error_t *
-unlock_cache(svn_membuffer_t *cache, svn_error_t *err)
-{
-#if APR_HAS_THREADS
-  if (cache->mutex)
-  {
-    apr_status_t status = apr_thread_mutex_unlock(cache->mutex);
-    if (err)
-      return err;
-
-    if (status)
-      return svn_error_wrap_apr(status, _("Can't unlock cache mutex"));
-  }
-#endif
-
-  return err;
-}
-
 /* Resolve a dictionary entry reference, i.e. return the entry
  * for the given IDX.
  */
@@ -713,7 +675,7 @@ initialize_group(svn_membuffer_t *cache,
 static entry_t *
 find_entry(svn_membuffer_t *cache,
            apr_uint32_t group_index,
-           unsigned char *to_find,
+           const unsigned char *to_find,
            svn_boolean_t find_empty)
 {
   entry_t *group;
@@ -1076,25 +1038,10 @@ svn_cache__membuffer_cache_create(svn_me
           return svn_error_wrap_apr(APR_ENOMEM, _("OOM"));
         }
 
-#if APR_HAS_THREADS
       /* A lock for intra-process synchronization to the cache, or NULL if
        * the cache's creator doesn't feel the cache needs to be
        * thread-safe. */
-
-      c[seg].mutex = NULL;
-      if (thread_safe)
-        {
-          apr_status_t status =
-              apr_thread_mutex_create(&(c[seg].mutex),
-                                      APR_THREAD_MUTEX_DEFAULT,
-                                      pool);
-          if (status)
-            return svn_error_wrap_apr(status, _("Can't create cache mutex"));
-        }
-#else
-      if (thread_safe)
-        return svn_error_wrap_apr(APR_ENOTIMPL, _("APR doesn't support threads"));
-#endif
+      SVN_ERR(svn_mutex__init(&c[seg].mutex, thread_safe, pool));
     }
 
   /* done here
@@ -1104,48 +1051,30 @@ svn_cache__membuffer_cache_create(svn_me
 }
 
 
-/* Try to insert the ITEM and use the KEY to unqiuely identify it.
+/* Try to insert the serialized item given in BUFFER with SIZE into
+ * the group GROUP_INDEX of CACHE and uniquely identify it by hash 
+ * value TO_FIND. 
+ * 
  * However, there is no guarantee that it will actually be put into
- * the cache. If there is already some data associated to the KEY,
+ * the cache. If there is already some data associated with TO_FIND,
  * it will be removed from the cache even if the new data cannot
  * be inserted.
- *
- * The SERIALIZER is called to transform the ITEM into a single,
- * flat data buffer. Temporary allocations may be done in POOL.
+ * 
+ * Note: This function requires the caller to serialization access.
+ * Don't call it directly, call membuffer_cache_get_partial instead.
  */
 static svn_error_t *
-membuffer_cache_set(svn_membuffer_t *cache,
-                    const void *key,
-                    apr_size_t key_len,
-                    void *item,
-                    svn_cache__serialize_func_t serializer,
-                    DEBUG_CACHE_MEMBUFFER_TAG_ARG
-                    apr_pool_t *scratch_pool)
+membuffer_cache_set_internal(svn_membuffer_t *cache,
+                             const unsigned char *to_find,
+                             apr_uint32_t group_index,
+                             char *buffer,
+                             apr_size_t size,
+                             DEBUG_CACHE_MEMBUFFER_TAG_ARG
+                             apr_pool_t *scratch_pool)
 {
-  apr_uint32_t group_index;
-  unsigned char to_find[KEY_SIZE];
-  entry_t *entry;
-  char *buffer;
-  apr_size_t size;
-
-  /* find the entry group that will hold the key.
-   */
-  group_index = get_group_index(&cache, key, key_len, to_find, scratch_pool);
-  if (group_index == NO_INDEX)
-    return SVN_NO_ERROR;
-
-  /* Serialize data data.
-   */
-  if (item)
-    SVN_ERR(serializer(&buffer, &size, item, scratch_pool));
-
-  /* The actual cache data access needs to sync'ed
-   */
-  SVN_ERR(lock_cache(cache));
-
   /* if necessary, enlarge the insertion window.
    */
-  if (   item != NULL
+  if (   buffer != NULL
       && cache->data_size / 4 > size
       && ensure_data_insertable(cache, size))
     {
@@ -1153,7 +1082,7 @@ membuffer_cache_set(svn_membuffer_t *cac
        * Get an unused entry for the key and and initialize it with
        * the serialized item's (future) posion within data buffer.
        */
-      entry = find_entry(cache, group_index, to_find, TRUE);
+      entry_t *entry = find_entry(cache, group_index, to_find, TRUE);
       entry->size = size;
       entry->offset = cache->current_data;
 
@@ -1182,44 +1111,76 @@ membuffer_cache_set(svn_membuffer_t *cac
        */
       find_entry(cache, group_index, to_find, TRUE);
     }
-
-  /* done here -> unlock the cache
-   */
-  return unlock_cache(cache, SVN_NO_ERROR);
+  return SVN_NO_ERROR;
 }
 
-/* Look for the *ITEM identified by KEY. If no item has been stored
- * for KEY, *ITEM will be NULL. Otherwise, the DESERIALIZER is called
- * re-construct the proper object from the serialized data.
- * Allocations will be done in POOL.
+/* Try to insert the ITEM and use the KEY to unqiuely identify it.
+ * However, there is no guarantee that it will actually be put into
+ * the cache. If there is already some data associated to the KEY,
+ * it will be removed from the cache even if the new data cannot
+ * be inserted.
+ *
+ * The SERIALIZER is called to transform the ITEM into a single,
+ * flat data buffer. Temporary allocations may be done in POOL.
  */
 static svn_error_t *
-membuffer_cache_get(svn_membuffer_t *cache,
+membuffer_cache_set(svn_membuffer_t *cache,
                     const void *key,
                     apr_size_t key_len,
-                    void **item,
-                    svn_cache__deserialize_func_t deserializer,
+                    void *item,
+                    svn_cache__serialize_func_t serializer,
                     DEBUG_CACHE_MEMBUFFER_TAG_ARG
-                    apr_pool_t *result_pool)
+                    apr_pool_t *scratch_pool)
 {
   apr_uint32_t group_index;
   unsigned char to_find[KEY_SIZE];
-  entry_t *entry;
-  char *buffer;
+  char *buffer = NULL;
   apr_size_t size;
 
   /* find the entry group that will hold the key.
    */
-  group_index = get_group_index(&cache, key, key_len, to_find, result_pool);
+  group_index = get_group_index(&cache, key, key_len, to_find, scratch_pool);
   if (group_index == NO_INDEX)
-    {
-      /* Some error occured, return "item not found".
-       */
-      *item = NULL;
-      return SVN_NO_ERROR;
-    }
+    return SVN_NO_ERROR;
+
+  /* Serialize data data.
+   */
+  if (item)
+    SVN_ERR(serializer(&buffer, &size, item, scratch_pool));
 
-  SVN_ERR(lock_cache(cache));
+  /* The actual cache data access needs to sync'ed
+   */
+  SVN_MUTEX__WITH_LOCK(cache->mutex,
+                       membuffer_cache_set_internal(cache,
+                                                    to_find,
+                                                    group_index,
+                                                    buffer,
+                                                    size,
+                                                    DEBUG_CACHE_MEMBUFFER_TAG
+                                                    scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+/* Look for the cache entry in group GROUP_INDEX of CACHE, identified
+ * by the hash value TO_FIND. If no item has been stored for KEY, 
+ * *BUFFER will be NULL. Otherwise, return a copy of the serialized
+ * data in *BUFFER and return its size in *ITEM_SIZE. Allocations will 
+ * be done in POOL.
+ * 
+ * Note: This function requires the caller to serialization access.
+ * Don't call it directly, call membuffer_cache_get_partial instead.
+ */
+static svn_error_t *
+membuffer_cache_get_internal(svn_membuffer_t *cache,
+                             apr_uint32_t group_index,
+                             const unsigned char *to_find,
+                             char **buffer,
+                             apr_size_t *item_size,
+                             DEBUG_CACHE_MEMBUFFER_TAG_ARG
+                             apr_pool_t *result_pool)
+{
+  entry_t *entry;
+  apr_size_t size;
 
   /* The actual cache data access needs to sync'ed
    */
@@ -1229,13 +1190,15 @@ membuffer_cache_get(svn_membuffer_t *cac
     {
       /* no such entry found.
        */
-      *item = NULL;
-      return unlock_cache(cache, SVN_NO_ERROR);
+      *buffer = NULL;
+      *item_size = 0;
+  
+      return SVN_NO_ERROR;
     }
-
+    
   size = ALIGN_VALUE(entry->size);
-  buffer = ALIGN_POINTER(apr_palloc(result_pool, size + ITEM_ALIGNMENT-1));
-  memcpy(buffer, (const char*)cache->data + entry->offset, size);
+  *buffer = ALIGN_POINTER(apr_palloc(result_pool, size + ITEM_ALIGNMENT-1));
+  memcpy(*buffer, (const char*)cache->data + entry->offset, size);
 
 #ifdef SVN_DEBUG_CACHE_MEMBUFFER
 
@@ -1258,45 +1221,91 @@ membuffer_cache_get(svn_membuffer_t *cac
   cache->hit_count++;
   cache->total_hits++;
 
-  SVN_ERR(unlock_cache(cache, SVN_NO_ERROR));
-
-  /* re-construct the original data object from its serialized form.
-   */
-  return deserializer(item, buffer, entry->size, result_pool);
+  *item_size = entry->size;
+  
+  return SVN_NO_ERROR;
 }
 
-/* Look for the cache entry identified by KEY and KEY_LEN. FOUND indicates
- * whether that entry exists. If not found, *ITEM will be NULL. Otherwise,
- * the DESERIALIZER is called with that entry and the BATON provided
- * and will extract the desired information. The result is set in *ITEM.
+/* Look for the *ITEM identified by KEY. If no item has been stored
+ * for KEY, *ITEM will be NULL. Otherwise, the DESERIALIZER is called
+ * re-construct the proper object from the serialized data.
  * Allocations will be done in POOL.
  */
 static svn_error_t *
-membuffer_cache_get_partial(svn_membuffer_t *cache,
-                            const void *key,
-                            apr_size_t key_len,
-                            void **item,
-                            svn_boolean_t *found,
-                            svn_cache__partial_getter_func_t deserializer,
-                            void *baton,
-                            DEBUG_CACHE_MEMBUFFER_TAG_ARG
-                            apr_pool_t *result_pool)
+membuffer_cache_get(svn_membuffer_t *cache,
+                    const void *key,
+                    apr_size_t key_len,
+                    void **item,
+                    svn_cache__deserialize_func_t deserializer,
+                    DEBUG_CACHE_MEMBUFFER_TAG_ARG
+                    apr_pool_t *result_pool)
 {
   apr_uint32_t group_index;
   unsigned char to_find[KEY_SIZE];
-  entry_t *entry;
-  svn_error_t *err = SVN_NO_ERROR;
+  char *buffer;
+  apr_size_t size;
 
+  /* find the entry group that will hold the key.
+   */
   group_index = get_group_index(&cache, key, key_len, to_find, result_pool);
+  if (group_index == NO_INDEX)
+    {
+      /* Some error occured, return "item not found".
+       */
+      *item = NULL;
+      return SVN_NO_ERROR;
+    }
 
-  SVN_ERR(lock_cache(cache));
+  SVN_MUTEX__WITH_LOCK(cache->mutex,
+                       membuffer_cache_get_internal(cache,
+                                                    group_index,
+                                                    to_find,
+                                                    &buffer,
+                                                    &size,
+                                                    DEBUG_CACHE_MEMBUFFER_TAG
+                                                    result_pool));
 
-  entry = find_entry(cache, group_index, to_find, FALSE);
+  /* re-construct the original data object from its serialized form.
+   */
+  if (buffer == NULL)
+    {
+      *item = NULL;
+      return SVN_NO_ERROR;
+    }
+    
+  return deserializer(item, buffer, size, result_pool);
+}
+
+/* Look for the cache entry in group GROUP_INDEX of CACHE, identified
+ * by the hash value TO_FIND. FOUND indicates whether that entry exists.
+ * If not found, *ITEM will be NULL.
+ * 
+ * Otherwise, the DESERIALIZER is called with that entry and the BATON 
+ * provided and will extract the desired information. The result is set
+ * in *ITEM. Allocations will be done in POOL.
+ * 
+ * Note: This function requires the caller to serialization access.
+ * Don't call it directly, call membuffer_cache_get_partial instead.
+ */
+static svn_error_t *
+membuffer_cache_get_partial_internal(svn_membuffer_t *cache,
+                                     apr_uint32_t group_index,
+                                     const unsigned char *to_find,
+                                     void **item,
+                                     svn_boolean_t *found,
+                                     svn_cache__partial_getter_func_t deserializer,
+                                     void *baton,
+                                     DEBUG_CACHE_MEMBUFFER_TAG_ARG
+                                     apr_pool_t *result_pool)
+{
+  entry_t *entry = find_entry(cache, group_index, to_find, FALSE);
   cache->total_reads++;
   if (entry == NULL)
     {
       *item = NULL;
       *found = FALSE;
+      
+      return SVN_NO_ERROR;
     }
   else
     {
@@ -1324,50 +1333,75 @@ membuffer_cache_get_partial(svn_membuffe
 
 #endif
 
-      err = deserializer(item,
-                         (const char*)cache->data + entry->offset,
-                         entry->size,
-                         baton,
-                         result_pool);
+      return deserializer(item,
+                          (const char*)cache->data + entry->offset,
+                          entry->size,
+                          baton,
+                          result_pool);
     }
-
-  /* done here -> unlock the cache
-   */
-  return unlock_cache(cache, err);
 }
 
-/* Look for the cache entry identified by KEY and KEY_LEN. If no entry
- * has been found, the function returns without modifying the cache.
- * Otherwise, FUNC is called with that entry and the BATON provided
- * and may modify the cache entry. Allocations will be done in POOL.
+/* Look for the cache entry identified by KEY and KEY_LEN. FOUND indicates
+ * whether that entry exists. If not found, *ITEM will be NULL. Otherwise,
+ * the DESERIALIZER is called with that entry and the BATON provided
+ * and will extract the desired information. The result is set in *ITEM.
+ * Allocations will be done in POOL.
  */
 static svn_error_t *
-membuffer_cache_set_partial(svn_membuffer_t *cache,
+membuffer_cache_get_partial(svn_membuffer_t *cache,
                             const void *key,
                             apr_size_t key_len,
-                            svn_cache__partial_setter_func_t func,
+                            void **item,
+                            svn_boolean_t *found,
+                            svn_cache__partial_getter_func_t deserializer,
                             void *baton,
                             DEBUG_CACHE_MEMBUFFER_TAG_ARG
-                            apr_pool_t *scratch_pool)
+                            apr_pool_t *result_pool)
 {
   apr_uint32_t group_index;
   unsigned char to_find[KEY_SIZE];
-  entry_t *entry;
-  svn_error_t *err = SVN_NO_ERROR;
 
-  /* cache item lookup
-   */
-  group_index = get_group_index(&cache, key, key_len, to_find, scratch_pool);
+  group_index = get_group_index(&cache, key, key_len, to_find, result_pool);
 
-  SVN_ERR(lock_cache(cache));
+  SVN_MUTEX__WITH_LOCK(cache->mutex,
+                       membuffer_cache_get_partial_internal
+                           (cache, group_index, to_find, item, found,
+                            deserializer, baton, DEBUG_CACHE_MEMBUFFER_TAG
+                            result_pool));
 
-  entry = find_entry(cache, group_index, to_find, FALSE);
+  return SVN_NO_ERROR;
+}
+
+/* Look for the cache entry in group GROUP_INDEX of CACHE, identified
+ * by the hash value TO_FIND. If no entry has been found, the function
+ * returns without modifying the cache.
+ * 
+ * Otherwise, FUNC is called with that entry and the BATON provided
+ * and may modify the cache entry. Allocations will be done in POOL.
+ * 
+ * Note: This function requires the caller to serialization access.
+ * Don't call it directly, call membuffer_cache_set_partial instead.
+ */
+static svn_error_t *
+membuffer_cache_set_partial_internal(svn_membuffer_t *cache,
+                                     apr_uint32_t group_index,
+                                     const unsigned char *to_find,
+                                     svn_cache__partial_setter_func_t func,
+                                     void *baton,
+                                     DEBUG_CACHE_MEMBUFFER_TAG_ARG
+                                     apr_pool_t *scratch_pool)
+{
+  /* cache item lookup
+   */
+  entry_t *entry = find_entry(cache, group_index, to_find, FALSE);
   cache->total_reads++;
 
   /* this function is a no-op if the item is not in cache
    */
   if (entry != NULL)
     {
+      svn_error_t *err;
+
       /* access the serialized cache item */
       char *data = (char*)cache->data + entry->offset;
       char *orig_data = data;
@@ -1441,9 +1475,39 @@ membuffer_cache_set_partial(svn_membuffe
         }
     }
 
+  return SVN_NO_ERROR;
+}
+
+/* Look for the cache entry identified by KEY and KEY_LEN. If no entry
+ * has been found, the function returns without modifying the cache.
+ * Otherwise, FUNC is called with that entry and the BATON provided
+ * and may modify the cache entry. Allocations will be done in POOL.
+ */
+static svn_error_t *
+membuffer_cache_set_partial(svn_membuffer_t *cache,
+                            const void *key,
+                            apr_size_t key_len,
+                            svn_cache__partial_setter_func_t func,
+                            void *baton,
+                            DEBUG_CACHE_MEMBUFFER_TAG_ARG
+                            apr_pool_t *scratch_pool)
+{
+  apr_uint32_t group_index;
+  unsigned char to_find[KEY_SIZE];
+
+  /* cache item lookup
+   */
+  group_index = get_group_index(&cache, key, key_len, to_find, scratch_pool);
+
+  SVN_MUTEX__WITH_LOCK(cache->mutex,
+                       membuffer_cache_set_partial_internal
+                           (cache, group_index, to_find, func, baton,
+                            DEBUG_CACHE_MEMBUFFER_TAG_ARG
+                            scratch_pool));
+
   /* done here -> unlock the cache
    */
-  return unlock_cache(cache, err);
+  return SVN_NO_ERROR;
 }
 
 /* Implement the svn_cache__t interface on top of a shared membuffer cache.
@@ -1652,6 +1716,8 @@ svn_membuffer_cache_iter(svn_boolean_t *
                           _("Can't iterate a membuffer-based cache"));
 }
 
+/* Implement svn_cache__vtable_t.get_partial
+ */
 static svn_error_t *
 svn_membuffer_cache_get_partial(void **value_p,
                                 svn_boolean_t *found,
@@ -1695,6 +1761,8 @@ svn_membuffer_cache_get_partial(void **v
   return SVN_NO_ERROR;
 }
 
+/* Implement svn_cache__vtable_t.set_partial
+ */
 static svn_error_t *
 svn_membuffer_cache_set_partial(void *cache_void,
                                 const void *key,
@@ -1728,6 +1796,8 @@ svn_membuffer_cache_set_partial(void *ca
   return SVN_NO_ERROR;
 }
 
+/* Implement svn_cache__vtable_t.is_cachable
+ */
 static svn_boolean_t
 svn_membuffer_cache_is_cachable(void *cache_void, apr_size_t size)
 {
@@ -1740,6 +1810,25 @@ svn_membuffer_cache_is_cachable(void *ca
       && (size < APR_UINT32_MAX - ITEM_ALIGNMENT);
 }
 
+/* Add statistics of SEGMENT to INFO.
+ */
+static svn_error_t *
+svn_membuffer_get_segment_info(svn_membuffer_t *segment,
+                               svn_cache__info_t *info)
+{
+  info->data_size += segment->data_size;
+  info->used_size += segment->data_used;
+  info->total_size += segment->data_size +
+      segment->group_count * GROUP_SIZE * sizeof(entry_t);
+
+  info->used_entries += segment->used_entries;
+  info->total_entries += segment->group_count * GROUP_SIZE;
+
+  return SVN_NO_ERROR;
+}
+
+/* Implement svn_cache__vtable_t.get_info
+ */
 static svn_error_t *
 svn_membuffer_cache_get_info(void *cache_void,
                              svn_cache__info_t *info,
@@ -1765,18 +1854,8 @@ svn_membuffer_cache_get_info(void *cache
   for (i = 0; i < cache->membuffer->segment_count; ++i)
     {
       svn_membuffer_t *segment = cache->membuffer + i;
-
-      SVN_ERR(lock_cache(segment));
-
-      info->data_size += segment->data_size;
-      info->used_size += segment->data_used;
-      info->total_size += segment->data_size +
-          segment->group_count * GROUP_SIZE * sizeof(entry_t);
-
-      info->used_entries += segment->used_entries;
-      info->total_entries += segment->group_count * GROUP_SIZE;
-
-      SVN_ERR(unlock_cache(segment, SVN_NO_ERROR));
+      SVN_MUTEX__WITH_LOCK(segment->mutex, 
+                           svn_membuffer_get_segment_info(segment, info));
     }
 
   return SVN_NO_ERROR;

Modified: subversion/branches/showing-merge-info/subversion/libsvn_subr/dso.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_subr/dso.c?rev=1182334&r1=1182333&r2=1182334&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_subr/dso.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_subr/dso.c Wed Oct 12 12:38:41 2011
@@ -26,10 +26,10 @@
 #include "svn_pools.h"
 #include "svn_private_config.h"
 
+#include "private/svn_mutex.h"
+
 /* A mutex to protect our global pool and cache. */
-#if APR_HAS_THREADS
-static apr_thread_mutex_t *dso_mutex;
-#endif
+static svn_mutex__t *dso_mutex = NULL;
 
 /* Global pool to allocate DSOs in. */
 static apr_pool_t *dso_pool;
@@ -49,40 +49,21 @@ static int not_there_sentinel;
 svn_error_t *
 svn_dso_initialize2(void)
 {
-#if APR_HAS_THREADS
-  apr_status_t status;
-#endif
   if (dso_pool)
     return SVN_NO_ERROR;
 
   dso_pool = svn_pool_create(NULL);
 
-#if APR_HAS_THREADS
-  status = apr_thread_mutex_create(&dso_mutex,
-                                   APR_THREAD_MUTEX_DEFAULT, dso_pool);
-  if (status)
-    return svn_error_wrap_apr(status, _("Can't create DSO mutex"));
-#endif
+  SVN_ERR(svn_mutex__init(&dso_mutex, TRUE, dso_pool));
 
   dso_cache = apr_hash_make(dso_pool);
   return SVN_NO_ERROR;
 }
 
 #if APR_HAS_DSO
-svn_error_t *
-svn_dso_load(apr_dso_handle_t **dso, const char *fname)
+static svn_error_t *
+svn_dso_load_internal(apr_dso_handle_t **dso, const char *fname)
 {
-  apr_status_t status;
-
-  if (! dso_pool)
-    SVN_ERR(svn_dso_initialize2());
-
-#if APR_HAS_THREADS
-  status = apr_thread_mutex_lock(dso_mutex);
-  if (status)
-    return svn_error_wrap_apr(status, _("Can't grab DSO mutex"));
-#endif
-
   *dso = apr_hash_get(dso_cache, fname, APR_HASH_KEY_STRING);
 
   /* First check to see if we've been through this before...  We do this
@@ -91,18 +72,13 @@ svn_dso_load(apr_dso_handle_t **dso, con
   if (*dso == NOT_THERE)
     {
       *dso = NULL;
-#if APR_HAS_THREADS
-      status = apr_thread_mutex_unlock(dso_mutex);
-      if (status)
-        return svn_error_wrap_apr(status, _("Can't ungrab DSO mutex"));
-#endif
       return SVN_NO_ERROR;
     }
 
   /* If we got nothing back from the cache, try and load the library. */
   if (! *dso)
     {
-      status = apr_dso_load(dso, fname, dso_pool);
+      apr_status_t status = apr_dso_load(dso, fname, dso_pool);
       if (status)
         {
 #ifdef SVN_DEBUG_DSO
@@ -120,11 +96,6 @@ svn_dso_load(apr_dso_handle_t **dso, con
                        APR_HASH_KEY_STRING,
                        NOT_THERE);
 
-#if APR_HAS_THREADS
-          status = apr_thread_mutex_unlock(dso_mutex);
-          if (status)
-            return svn_error_wrap_apr(status, _("Can't ungrab DSO mutex"));
-#endif
           return SVN_NO_ERROR;
         }
 
@@ -135,11 +106,16 @@ svn_dso_load(apr_dso_handle_t **dso, con
                    *dso);
     }
 
-#if APR_HAS_THREADS
-  status = apr_thread_mutex_unlock(dso_mutex);
-  if (status)
-    return svn_error_wrap_apr(status, _("Can't ungrab DSO mutex"));
-#endif
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_dso_load(apr_dso_handle_t **dso, const char *fname)
+{
+  if (! dso_pool)
+    SVN_ERR(svn_dso_initialize2());
+
+  SVN_MUTEX__WITH_LOCK(dso_mutex, svn_dso_load_internal(dso, fname));
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/showing-merge-info/subversion/libsvn_subr/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_subr/mergeinfo.c?rev=1182334&r1=1182333&r2=1182334&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_subr/mergeinfo.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_subr/mergeinfo.c Wed Oct 12 12:38:41 2011
@@ -742,68 +742,401 @@ svn_mergeinfo_parse(svn_mergeinfo_t *mer
   return err;
 }
 
+/* Cleanup after svn_rangelist_merge2 when it modifies the ending range of
+   a single rangelist element in-place.
+
+   If *RANGE_INDEX is not a valid element in RANGELIST do nothing.  Otherwise
+   ensure that RANGELIST[*RANGE_INDEX]->END does not adjoin or overlap any
+   subsequent ranges in RANGELIST.
+
+   If overlap is found, then remove, modify, and/or add elements to RANGELIST
+   as per the invariants for rangelists documented in svn_mergeinfo.h.  If
+   RANGELIST[*RANGE_INDEX]->END adjoins a subsequent element then combine the
+   elements if their inheritability permits -- The inheritance of intersecting
+   and adjoining ranges is handled as per svn_mergeinfo_merge2.  Upon return
+   set *RANGE_INDEX to the index of the youngest element modified, added, or
+   adjoined to RANGELIST[*RANGE_INDEX].
+
+   Note: Adjoining rangelist elements are those where the end rev of the older
+   element is equal to the start rev of the younger element.
+
+   Any new elements inserted into RANGELIST are allocated in  RESULT_POOL.*/
+static void
+adjust_remaining_ranges(apr_array_header_t *rangelist,
+                        int *range_index,
+                        apr_pool_t *result_pool)
+{
+  int i;
+  int starting_index;
+  int elements_to_delete = 0;
+  svn_merge_range_t *modified_range;
+
+  if (*range_index >= rangelist->nelts)
+    return;
+
+  starting_index = *range_index + 1;
+  modified_range = APR_ARRAY_IDX(rangelist, *range_index, svn_merge_range_t *);
+
+  for (i = *range_index + 1; i < rangelist->nelts; i++)
+    {
+      svn_merge_range_t *next_range = APR_ARRAY_IDX(rangelist, i,
+                                                    svn_merge_range_t *);
+
+      /* If MODIFIED_RANGE doesn't adjoin or overlap the next range in
+         RANGELIST then we are finished. */
+      if (modified_range->end < next_range->start)
+        break;
+
+      /* Does MODIFIED_RANGE adjoin NEXT_RANGE? */
+      if (modified_range->end == next_range->start)
+        {
+          if (modified_range->inheritable == next_range->inheritable)
+            {
+              /* Combine adjoining ranges with the same inheritability. */
+              modified_range->end = next_range->end;
+              elements_to_delete++;
+            }
+          else
+            {
+              /* Cannot join because inheritance differs. */
+              (*range_index)++;
+            }
+          break;
+        }
+
+      /* Alright, we know MODIFIED_RANGE overlaps NEXT_RANGE, but how? */
+      if (modified_range->end > next_range->end)
+        {
+          /* NEXT_RANGE is a proper subset of MODIFIED_RANGE and the two
+             don't share the same end range. */
+          if (modified_range->inheritable
+              || (modified_range->inheritable == next_range->inheritable))
+            {
+              /* MODIFIED_RANGE absorbs NEXT_RANGE. */
+              elements_to_delete++;
+            }
+          else
+            {
+              /* NEXT_RANGE is a proper subset MODIFIED_RANGE but
+                 MODIFIED_RANGE is non-inheritable and NEXT_RANGE is
+                 inheritable.  This means MODIFIED_RANGE is truncated,
+                 NEXT_RANGE remains, and the portion of MODIFIED_RANGE
+                 younger than NEXT_RANGE is added as a separate range:
+                  ______________________________________________
+                 |                                              |
+                 M                 MODIFIED_RANGE               N
+                 |                 (!inhertiable)               |
+                 |______________________________________________|
+                                  |              |
+                                  O  NEXT_RANGE  P
+                                  | (inheritable)|
+                                  |______________|
+                                         |
+                                         V
+                  _______________________________________________
+                 |                |              |               |
+                 M MODIFIED_RANGE O  NEXT_RANGE  P   NEW_RANGE   N
+                 | (!inhertiable) | (inheritable)| (!inheritable)|
+                 |________________|______________|_______________|
+              */
+              svn_merge_range_t *new_modified_range =
+                apr_palloc(result_pool, sizeof(*new_modified_range));
+              new_modified_range->start = next_range->end;
+              new_modified_range->end = modified_range->end;
+              new_modified_range->inheritable = FALSE;
+              modified_range->end = next_range->start;
+              (*range_index)+=2;
+              svn_sort__array_insert(&new_modified_range, rangelist,
+                                     *range_index);
+              /* Recurse with the new range. */
+              adjust_remaining_ranges(rangelist, range_index, result_pool);
+              break;
+            }
+        }
+      else if (modified_range->end == next_range->end)
+        {
+          /* NEXT_RANGE is a proper subset MODIFIED_RANGE and share
+             the same end range. */
+          if (modified_range->inheritable
+              || (modified_range->inheritable == next_range->inheritable))
+            {
+              /* MODIFIED_RANGE absorbs NEXT_RANGE. */
+              elements_to_delete++;
+            }
+          else
+            {
+              /* The intersection between MODIFIED_RANGE and NEXT_RANGE is
+                 absorbed by the latter. */
+              modified_range->end = next_range->start;
+              (*range_index)++;
+            }
+          break;
+        }
+      else
+        {
+          /* NEXT_RANGE and MODIFIED_RANGE intersect but NEXT_RANGE is not
+             a proper subset of MODIFIED_RANGE, nor do the two share the
+             same end revision, i.e. they overlap. */
+          if (modified_range->inheritable == next_range->inheritable)
+            {
+              /* Combine overlapping ranges with the same inheritability. */
+              modified_range->end = next_range->end;
+              elements_to_delete++;
+            }
+          else if (modified_range->inheritable)
+            {
+              /* MODIFIED_RANGE absorbs the portion of NEXT_RANGE it overlaps
+                 and NEXT_RANGE is truncated. */
+              next_range->start = modified_range->end;
+              (*range_index)++;
+            }
+          else
+            {
+              /* NEXT_RANGE absorbs the portion of MODIFIED_RANGE it overlaps
+                 and MODIFIED_RANGE is truncated. */
+              modified_range->end = next_range->start;
+              (*range_index)++;
+            }
+          break;
+        }
+    }
+
+  if (elements_to_delete)
+    svn_sort__array_delete(rangelist, starting_index, elements_to_delete);
+}
+
 svn_error_t *
 svn_rangelist_merge2(apr_array_header_t *rangelist,
                      const apr_array_header_t *changes,
                      apr_pool_t *result_pool,
                      apr_pool_t *scratch_pool)
 {
-  int i, j;
-  apr_array_header_t *original_rangelist;
-  
-  original_rangelist = apr_array_copy(scratch_pool, rangelist);
-  apr_array_clear(rangelist);
+  int i = 0;
+  int j = 0;
 
-  i = 0;
-  j = 0;
-  while (i < (original_rangelist)->nelts && j < changes->nelts)
-    {
-      svn_merge_range_t *elt1, *elt2;
-      int res;
+  /* We may modify CHANGES, so make a copy in SCRATCH_POOL. */
+  changes = svn_rangelist_dup(changes, scratch_pool);
 
-      elt1 = APR_ARRAY_IDX(original_rangelist, i, svn_merge_range_t *);
-      elt2 = APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
+  while (i < rangelist->nelts && j < changes->nelts)
+    {
+      svn_merge_range_t *range =
+        APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
+      svn_merge_range_t *change =
+        APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
+      int res = svn_sort_compare_ranges(&range, &change);
 
-      res = svn_sort_compare_ranges(&elt1, &elt2);
       if (res == 0)
         {
           /* Only when merging two non-inheritable ranges is the result also
              non-inheritable.  In all other cases ensure an inheritiable
              result. */
-          if (elt1->inheritable || elt2->inheritable)
-            elt1->inheritable = TRUE;
-          SVN_ERR(combine_with_lastrange(elt1, rangelist, TRUE, result_pool));
+          if (range->inheritable || change->inheritable)
+            range->inheritable = TRUE;
           i++;
           j++;
         }
-      else if (res < 0)
+      else if (res < 0) /* CHANGE is younger than RANGE */
         {
-          SVN_ERR(combine_with_lastrange(elt1, rangelist, TRUE, result_pool));
-          i++;
+          if (range->end < change->start)
+            {
+              /* RANGE is older than CHANGE and the two do not
+                 adjoin or overlap */
+              i++;
+            }
+          else if (range->end == change->start)
+            {
+              /* RANGE and CHANGE adjoin */
+              if (range->inheritable == change->inheritable)
+                {
+                  /* RANGE and CHANGE have the same inheritability so
+                     RANGE expands to absord CHANGE. */
+                  range->end = change->end;
+                  adjust_remaining_ranges(rangelist, &i, result_pool);
+                  j++;
+                }
+              else
+                {
+                  /* RANGE and CHANGE adjoin, but have different
+                     inheritability.  Since RANGE is older, just
+                     move on to the next RANGE. */
+                  i++;
+                }
+            }
+          else
+            {
+              /* RANGE and CHANGE overlap, but how? */
+              if ((range->inheritable == change->inheritable)
+                  || range->inheritable)
+                {
+                  /* If CHANGE is a proper subset of RANGE, it absorbs RANGE
+                      with no adjustment otherwise only the intersection is
+                      absorbed and CHANGE is truncated. */
+                  if (range->end >= change->end)
+                    j++;
+                  else
+                    change->start = range->end;
+                }
+              else
+                {
+                  /* RANGE is non-inheritable and CHANGE is inheritable. */
+                  if (range->start < change->start)
+                    {
+                      /* CHANGE absorbs intersection with RANGE and RANGE
+                         is truncated. */
+                      svn_merge_range_t *range_copy =
+                        svn_merge_range_dup(range, result_pool);
+                      range_copy->end = change->start;
+                      range->start = change->start;
+                      svn_sort__array_insert(&range_copy, rangelist, i++);
+                    }
+                  else
+                    {
+                      /* CHANGE and RANGE share the same start rev, but
+                         RANGE is considered older because its end rev
+                         is older. */
+                      range->inheritable = TRUE;
+                      change->start = range->end;
+                    }
+                }
+            }
         }
-      else
+      else /* res > 0, CHANGE is older than RANGE */
         {
-          SVN_ERR(combine_with_lastrange(elt2, rangelist, TRUE, result_pool));
-          j++;
+          if (change->end < range->start)
+            {
+              /* CHANGE is older than RANGE and the two do not
+                 adjoin or overlap, so insert a copy of CHANGE
+                 into RANGELIST. */
+              svn_merge_range_t *change_copy =
+                svn_merge_range_dup(change, result_pool);
+              svn_sort__array_insert(&change_copy, rangelist, i++);
+              j++;
+            }
+          else if (change->end == range->start)
+            {
+              /* RANGE and CHANGE adjoin */
+              if (range->inheritable == change->inheritable)
+                {
+                  /* RANGE and CHANGE have the same inheritability so we
+                     can simply combine the two in place. */
+                  range->start = change->start;
+                  j++;
+                }
+              else
+                {
+                  /* RANGE and CHANGE have different inheritability so insert
+                     a copy of CHANGE into RANGELIST. */
+                  svn_merge_range_t *change_copy =
+                    svn_merge_range_dup(change, result_pool);
+                  svn_sort__array_insert(&change_copy, rangelist, i);
+                  j++;
+                }
+            }
+          else
+            {
+              /* RANGE and CHANGE overlap. */
+              if (range->inheritable == change->inheritable)
+                {
+                  /* RANGE and CHANGE have the same inheritability so we
+                     can simply combine the two in place... */
+                  range->start = change->start;
+                  if (range->end < change->end)
+                    {
+                      /* ...but if RANGE is expanded ensure that we don't
+                         violate any rangelist invariants. */
+                      range->end = change->end;
+                      adjust_remaining_ranges(rangelist, &i, result_pool);
+                    }
+                  j++;
+                }
+              else if (range->inheritable)
+                {
+                  if (change->start < range->start)
+                    {
+                      /* RANGE is inheritable so absorbs any part of CHANGE
+                         it overlaps.  CHANGE is truncated and the remainder
+                         inserted into RANGELIST. */
+                      svn_merge_range_t *change_copy =
+                        svn_merge_range_dup(change, result_pool);
+                      change_copy->end = range->start;
+                      change->start = range->start;
+                      svn_sort__array_insert(&change_copy, rangelist, i++);
+                    }
+                  else
+                    {
+                      /* CHANGE and RANGE share the same start rev, but
+                         CHANGE is considered older because CHANGE->END is
+                         older than RANGE->END. */
+                      j++;
+                    }
+                }
+              else
+                {
+                  /* RANGE is non-inheritable and CHANGE is inheritable. */
+                  if (change->start < range->start)
+                    {
+                      if (change->end == range->end)
+                        {
+                          /* RANGE is a proper subset of CHANGE and share the
+                             same end revision, so set RANGE equal to CHANGE. */
+                          range->start = change->start;
+                          range->inheritable = TRUE;
+                          j++;
+                        }
+                      else if (change->end > range->end)
+                        {
+                          /* RANGE is a proper subset of CHANGE and CHANGE has
+                             a younger end revision, so set RANGE equal to its
+                             intersection with CHANGE and truncate CHANGE. */
+                          range->start = change->start;
+                          range->inheritable = TRUE;
+                          change->start = range->end;
+                        }
+                      else
+                        {
+                          /* CHANGE and RANGE overlap. Set RANGE equal to its
+                             intersection with CHANGE and take the remainder
+                             of RANGE and insert it into RANGELIST. */
+                          svn_merge_range_t *range_copy =
+                            svn_merge_range_dup(range, result_pool);
+                          range_copy->start = change->end;
+                          range->start = change->start;
+                          range->end = change->end;
+                          range->inheritable = TRUE;
+                          svn_sort__array_insert(&range_copy, rangelist, ++i);
+                          j++;
+                        }
+                    }
+                  else 
+                    {
+                      /* CHANGE and RANGE share the same start rev, but
+                         CHANGE is considered older because its end rev
+                         is older.
+                         
+                         Insert the intersection of RANGE and CHANGE into
+                         RANGELIST and then set RANGE to the non-intersecting
+                         portion of RANGE. */
+                      svn_merge_range_t *range_copy =
+                        svn_merge_range_dup(range, result_pool);
+                      range_copy->end = change->end;
+                      range_copy->inheritable = TRUE;
+                      range->start = change->end;
+                      svn_sort__array_insert(&range_copy, rangelist, i++);
+                      j++;
+                    }
+                }
+            }
         }
     }
-  /* Copy back any remaining elements.
-     Only one of these loops should end up running, if anything. */
-
-  SVN_ERR_ASSERT(!(i < (original_rangelist)->nelts && j < changes->nelts));
 
-  for (; i < (original_rangelist)->nelts; i++)
+  /* Copy any remaining elements in CHANGES into RANGELIST. */
+  for (; j < (changes)->nelts; j++)
     {
-      svn_merge_range_t *elt = APR_ARRAY_IDX(original_rangelist, i,
-                                             svn_merge_range_t *);
-      SVN_ERR(combine_with_lastrange(elt, rangelist, TRUE, result_pool));
-    }
-
-
-  for (; j < changes->nelts; j++)
-    {
-      svn_merge_range_t *elt = APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
-      SVN_ERR(combine_with_lastrange(elt, rangelist, TRUE, result_pool));
+      svn_merge_range_t *change =
+        APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
+      svn_merge_range_t *change_copy = svn_merge_range_dup(change,
+                                                           result_pool);
+      svn_sort__array_insert(&change_copy, rangelist, rangelist->nelts);
     }
 
   return SVN_NO_ERROR;
@@ -2280,3 +2613,27 @@ svn_mergeinfo__mergeinfo_from_segments(s
   *mergeinfo_p = mergeinfo;
   return SVN_NO_ERROR;
 }
+
+const char *
+svn_inheritance_to_word(svn_mergeinfo_inheritance_t inherit)
+{
+  switch (inherit)
+    {
+    case svn_mergeinfo_inherited:
+      return "inherited";
+    case svn_mergeinfo_nearest_ancestor:
+      return "nearest-ancestor";
+    default:
+      return "explicit";
+    }
+}
+
+svn_mergeinfo_inheritance_t
+svn_inheritance_from_word(const char *word)
+{
+  if (strcmp(word, "inherited") == 0)
+    return svn_mergeinfo_inherited;
+  if (strcmp(word, "nearest-ancestor") == 0)
+    return svn_mergeinfo_nearest_ancestor;
+  return svn_mergeinfo_explicit;
+}

Modified: subversion/branches/showing-merge-info/subversion/libsvn_subr/properties.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_subr/properties.c?rev=1182334&r1=1182333&r2=1182334&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_subr/properties.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_subr/properties.c Wed Oct 12 12:38:41 2011
@@ -221,6 +221,85 @@ svn_prop_diffs(apr_array_header_t **prop
   return SVN_NO_ERROR;
 }
 
+/**
+ * Reallocate the members of PROP using POOL.
+ */
+static void
+svn_prop__members_dup(svn_prop_t *prop, apr_pool_t *pool)
+{
+  if (prop->name)
+    prop->name = apr_pstrdup(pool, prop->name);
+  if (prop->value)
+    prop->value = svn_string_dup(prop->value, pool);
+}
+
+svn_prop_t *
+svn_prop_dup(const svn_prop_t *prop, apr_pool_t *pool)
+{
+  svn_prop_t *new_prop = apr_palloc(pool, sizeof(*new_prop));
+
+  *new_prop = *prop;
+
+  svn_prop__members_dup(new_prop, pool);
+
+  return new_prop;
+}
+
+apr_array_header_t *
+svn_prop_array_dup(const apr_array_header_t *array, apr_pool_t *pool)
+{
+  int i;
+  apr_array_header_t *new_array = apr_array_copy(pool, array);
+  for (i = 0; i < new_array->nelts; ++i)
+    {
+      svn_prop_t *elt = &APR_ARRAY_IDX(new_array, i, svn_prop_t);
+      svn_prop__members_dup(elt, pool);
+    }
+  return new_array;
+}
+
+apr_array_header_t *
+svn_prop_hash_to_array(apr_hash_t *hash, apr_pool_t *pool)
+{
+  apr_hash_index_t *hi;
+  apr_array_header_t *array = apr_array_make(pool, apr_hash_count(hash),
+                                             sizeof(svn_prop_t));
+
+  for (hi = apr_hash_first(pool, hash); hi; hi = apr_hash_next(hi))
+    {
+      const void *key;
+      void *val;
+      svn_prop_t prop;
+
+      apr_hash_this(hi, &key, NULL, &val);
+      prop.name = key;
+      prop.value = val;
+      APR_ARRAY_PUSH(array, svn_prop_t) = prop;
+    }
+
+  return array;
+}
+
+apr_hash_t *
+svn_prop_hash_dup(apr_hash_t *hash,
+                  apr_pool_t *pool)
+{
+  apr_hash_index_t *hi;
+  apr_hash_t *new_hash = apr_hash_make(pool);
+
+  for (hi = apr_hash_first(pool, hash); hi; hi = apr_hash_next(hi))
+    {
+      const void *key;
+      apr_ssize_t klen;
+      void *prop;
+
+      apr_hash_this(hi, &key, &klen, &prop);
+      apr_hash_set(new_hash, apr_pstrdup(pool, key), klen,
+                   svn_string_dup(prop, pool));
+    }
+  return new_hash;
+}
+
 apr_hash_t *
 svn_prop_array_to_hash(const apr_array_header_t *properties,
                        apr_pool_t *pool)

Modified: subversion/branches/showing-merge-info/subversion/libsvn_subr/sorts.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_subr/sorts.c?rev=1182334&r1=1182333&r2=1182334&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_subr/sorts.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_subr/sorts.c Wed Oct 12 12:38:41 2011
@@ -245,3 +245,35 @@ svn_sort__array_insert(const void *new_e
   /* Copy in the new element */
   memcpy(new_position, new_element, array->elt_size);
 }
+
+void
+svn_sort__array_delete(apr_array_header_t *arr,
+                       int delete_index,
+                       int elements_to_delete)
+{
+  /* Do we have a valid index and are there enough elements? */
+  if (delete_index >= 0
+      && delete_index < arr->nelts
+      && elements_to_delete > 0
+      && (elements_to_delete + delete_index) <= arr->nelts)
+    {
+      if (delete_index == (arr->nelts - 1))
+        {
+          /* Deleting the last or only element in an array is easy. */
+          apr_array_pop(arr);
+        }
+      else if ((delete_index + elements_to_delete) == arr->nelts)
+        {
+          /* Delete the last ELEMENTS_TO_DELETE elements. */
+          arr->nelts -= elements_to_delete;
+        }
+      else
+        {
+          memmove(
+            arr->elts + arr->elt_size * delete_index,
+            arr->elts + (arr->elt_size * (delete_index + elements_to_delete)),
+            arr->elt_size * (arr->nelts - elements_to_delete - delete_index));
+          arr->nelts -= elements_to_delete;
+        }
+    }
+}

Modified: subversion/branches/showing-merge-info/subversion/libsvn_subr/sqlite.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_subr/sqlite.c?rev=1182334&r1=1182333&r2=1182334&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_subr/sqlite.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_subr/sqlite.c Wed Oct 12 12:38:41 2011
@@ -637,12 +637,6 @@ init_sqlite(void *baton, apr_pool_t *poo
 
 #endif /* APR_HAS_THRADS */
 
-  /* SQLite 3.5 allows sharing cache instances, even in a multithreaded
-     environment. This allows sharing cached data when we open a database
-     more than once (Very common in the current pre-single-database state) */
-  SQLITE_ERR_MSG(sqlite3_enable_shared_cache(TRUE),
-                 _("Could not initialize SQLite shared cache"));
-
   return SVN_NO_ERROR;
 }
 
@@ -673,6 +667,13 @@ internal_open(sqlite3 **db3, const char 
     flags |= SQLITE_OPEN_NOMUTEX;
 #endif
 
+    /* SQLite 3.5 allows sharing cache instances, even in a multithreaded
+       environment. This allows sharing cached data when we open a database
+       more than once.
+
+       OS X 10.7 doesn't support sqlite3_enable_shared_cache. */
+    flags |= SQLITE_OPEN_SHAREDCACHE;
+
     /* Open the database. Note that a handle is returned, even when an error
        occurs (except for out-of-memory); thus, we can safely use it to
        extract an error message and construct an svn_error_t. */

Modified: subversion/branches/showing-merge-info/subversion/libsvn_subr/utf.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_subr/utf.c?rev=1182334&r1=1182333&r2=1182334&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_subr/utf.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_subr/utf.c Wed Oct 12 12:38:41 2011
@@ -42,6 +42,7 @@
 #include "private/svn_utf_private.h"
 #include "private/svn_dep_compat.h"
 #include "private/svn_string_private.h"
+#include "private/svn_mutex.h"
 
 
 
@@ -53,9 +54,7 @@ static const char *SVN_UTF_UTON_XLATE_HA
 
 static const char *SVN_APR_UTF8_CHARSET = "UTF-8";
 
-#if APR_HAS_THREADS
-static apr_thread_mutex_t *xlate_handle_mutex = NULL;
-#endif
+static svn_mutex__t *xlate_handle_mutex = NULL;
 
 /* The xlate handle cache is a global hash table with linked lists of xlate
  * handles.  In multi-threaded environments, a thread "borrows" an xlate
@@ -99,10 +98,6 @@ xlate_cleanup(void *arg)
 {
   /* We set the cache variables to NULL so that translation works in other
      cleanup functions, even if it isn't cached then. */
-#if APR_HAS_THREADS
-  apr_thread_mutex_destroy(xlate_handle_mutex);
-  xlate_handle_mutex = NULL;
-#endif
   xlate_handle_hash = NULL;
 
   /* ensure no stale objects get accessed */
@@ -125,27 +120,24 @@ xlate_handle_node_cleanup(void *arg)
 void
 svn_utf_initialize(apr_pool_t *pool)
 {
-  apr_pool_t *subpool;
-#if APR_HAS_THREADS
-  apr_thread_mutex_t *mutex;
-#endif
-
   if (!xlate_handle_hash)
     {
       /* We create our own subpool, which we protect with the mutex.
          We can't use the pool passed to us by the caller, since we will
          use it for xlate handle allocations, possibly in multiple threads,
          and pool allocation is not thread-safe. */
-      subpool = svn_pool_create(pool);
-#if APR_HAS_THREADS
-      if (apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_DEFAULT, subpool)
-          == APR_SUCCESS)
-        xlate_handle_mutex = mutex;
-      else
-        return;
-#endif
+      apr_pool_t *subpool = svn_pool_create(pool);
+      svn_mutex__t *mutex;
+      svn_error_t *err = svn_mutex__init(&mutex, TRUE, subpool);
+      if (err)
+        {
+          svn_error_clear(err);
+          return;
+        }
 
+      xlate_handle_mutex = mutex;
       xlate_handle_hash = apr_hash_make(subpool);
+
       apr_pool_cleanup_register(subpool, NULL, xlate_cleanup,
                                 apr_pool_cleanup_null);
     }
@@ -202,91 +194,17 @@ atomic_swap(void * volatile * mem, void 
 #endif
 }
 
-/* Set *RET to a handle node for converting from FROMPAGE to TOPAGE,
-   creating the handle node if it doesn't exist in USERDATA_KEY.
-   If a node is not cached and apr_xlate_open() returns APR_EINVAL or
-   APR_ENOTIMPL, set (*RET)->handle to NULL.  If fail for any other
-   reason, return the error.
-
-   Allocate *RET and its xlate handle in POOL if svn_utf_initialize()
-   hasn't been called or USERDATA_KEY is NULL.  Else, allocate them
-   in the pool of xlate_handle_hash. */
+/* Set *RET to a newly created handle node for converting from FROMPAGE 
+   to TOPAGE, If apr_xlate_open() returns APR_EINVAL or APR_ENOTIMPL, set 
+   (*RET)->handle to NULL.  If fail for any other reason, return the error.
+   Allocate *RET and its xlate handle in POOL. */
 static svn_error_t *
-get_xlate_handle_node(xlate_handle_node_t **ret,
-                      const char *topage, const char *frompage,
-                      const char *userdata_key, apr_pool_t *pool)
+xlate_alloc_handle(xlate_handle_node_t **ret,
+                   const char *topage, const char *frompage,
+                   apr_pool_t *pool)
 {
-  xlate_handle_node_t **old_node_p;
-  xlate_handle_node_t *old_node = NULL;
   apr_status_t apr_err;
   apr_xlate_t *handle;
-  svn_error_t *err = NULL;
-
-  /* If we already have a handle, just return it. */
-  if (userdata_key)
-    {
-      if (xlate_handle_hash)
-        {
-          /* 1st level: global, static items */
-          if (userdata_key == SVN_UTF_NTOU_XLATE_HANDLE)
-            old_node = atomic_swap(&xlat_ntou_static_handle, NULL);
-          else if (userdata_key == SVN_UTF_UTON_XLATE_HANDLE)
-            old_node = atomic_swap(&xlat_uton_static_handle, NULL);
-
-          if (old_node && old_node->valid)
-            {
-              *ret = old_node;
-              return SVN_NO_ERROR;
-            }
-
-          /* 2nd level: hash lookup */
-#if APR_HAS_THREADS
-          apr_err = apr_thread_mutex_lock(xlate_handle_mutex);
-          if (apr_err != APR_SUCCESS)
-            return svn_error_create(apr_err, NULL,
-                                    _("Can't lock charset translation mutex"));
-#endif
-          old_node_p = apr_hash_get(xlate_handle_hash, userdata_key,
-                                    APR_HASH_KEY_STRING);
-          if (old_node_p)
-            old_node = *old_node_p;
-          if (old_node)
-            {
-              /* Ensure that the handle is still valid. */
-              if (old_node->valid)
-                {
-                  /* Remove from the list. */
-                  *old_node_p = old_node->next;
-                  old_node->next = NULL;
-#if APR_HAS_THREADS
-                  apr_err = apr_thread_mutex_unlock(xlate_handle_mutex);
-                  if (apr_err != APR_SUCCESS)
-                    return svn_error_create(apr_err, NULL,
-                                            _("Can't unlock charset "
-                                              "translation mutex"));
-#endif
-                  *ret = old_node;
-                  return SVN_NO_ERROR;
-                }
-            }
-        }
-      else
-        {
-          void *p;
-          /* We fall back on a per-pool cache instead. */
-          apr_pool_userdata_get(&p, userdata_key, pool);
-          old_node = p;
-          /* Ensure that the handle is still valid. */
-          if (old_node && old_node->valid)
-            {
-              *ret = old_node;
-              return SVN_NO_ERROR;
-            }
-        }
-    }
-
-  /* Note that we still have the mutex locked (if it is initialized), so we
-     can use the global pool for creating the new xlate handle. */
 
   /* The error handling doesn't support the following cases, since we don't
      use them currently.  Catch this here. */
@@ -295,10 +213,6 @@ get_xlate_handle_node(xlate_handle_node_
                  && (frompage != SVN_APR_LOCALE_CHARSET
                      || topage != SVN_APR_LOCALE_CHARSET));
 
-  /* Use the correct pool for creating the handle. */
-  if (userdata_key && xlate_handle_hash)
-    pool = apr_hash_pool_get(xlate_handle_hash);
-
   /* Try to create a handle. */
 #if defined(WIN32)
   apr_err = svn_subr__win32_xlate_open((win32_xlate_t **)&handle, topage,
@@ -327,8 +241,7 @@ get_xlate_handle_node(xlate_handle_node_
                               _("Can't create a character converter from "
                                 "'%s' to '%s'"), frompage, topage);
 
-      err = svn_error_create(apr_err, NULL, errstr);
-      goto cleanup;
+      return svn_error_create(apr_err, NULL, errstr);
     }
 
   /* Allocate and initialize the node. */
@@ -350,76 +263,178 @@ get_xlate_handle_node(xlate_handle_node_
     apr_pool_cleanup_register(pool, *ret, xlate_handle_node_cleanup,
                               apr_pool_cleanup_null);
 
- cleanup:
-  /* Don't need the lock anymore. */
-#if APR_HAS_THREADS
+  return SVN_NO_ERROR;
+}
+
+/* Extend xlate_alloc_handle by using USERDATA_KEY as a key in our
+   global hash map, if available.
+   
+   Allocate *RET and its xlate handle in POOL if svn_utf_initialize()
+   hasn't been called or USERDATA_KEY is NULL.  Else, allocate them
+   in the pool of xlate_handle_hash.
+   
+   Note: this function is not thread-safe. Call get_xlate_handle_node
+   instead. */
+static svn_error_t *
+get_xlate_handle_node_internal(xlate_handle_node_t **ret,
+                               const char *topage, const char *frompage,
+                               const char *userdata_key, apr_pool_t *pool)
+{
+  /* If we already have a handle, just return it. */
   if (userdata_key && xlate_handle_hash)
     {
-      apr_status_t unlock_err = apr_thread_mutex_unlock(xlate_handle_mutex);
-      if (unlock_err != APR_SUCCESS)
-        return svn_error_create(unlock_err, NULL,
-                                _("Can't unlock charset translation mutex"));
+      xlate_handle_node_t *old_node = NULL;
+
+      /* 2nd level: hash lookup */
+      xlate_handle_node_t **old_node_p = apr_hash_get(xlate_handle_hash, 
+                                                      userdata_key,
+                                                      APR_HASH_KEY_STRING);
+      if (old_node_p)
+        old_node = *old_node_p;
+      if (old_node)
+        {
+          /* Ensure that the handle is still valid. */
+          if (old_node->valid)
+            {
+              /* Remove from the list. */
+              *old_node_p = old_node->next;
+              old_node->next = NULL;
+              *ret = old_node;
+              return SVN_NO_ERROR;
+            }
+        }
+    }
+
+  /* Note that we still have the mutex locked (if it is initialized), so we
+     can use the global pool for creating the new xlate handle. */
+
+  /* Use the correct pool for creating the handle. */
+  pool = apr_hash_pool_get(xlate_handle_hash);
+
+  return xlate_alloc_handle(ret, topage, frompage, pool);
+}
+
+/* Set *RET to a handle node for converting from FROMPAGE to TOPAGE,
+   creating the handle node if it doesn't exist in USERDATA_KEY.
+   If a node is not cached and apr_xlate_open() returns APR_EINVAL or
+   APR_ENOTIMPL, set (*RET)->handle to NULL.  If fail for any other
+   reason, return the error.
+
+   Allocate *RET and its xlate handle in POOL if svn_utf_initialize()
+   hasn't been called or USERDATA_KEY is NULL.  Else, allocate them
+   in the pool of xlate_handle_hash. */
+static svn_error_t *
+get_xlate_handle_node(xlate_handle_node_t **ret,
+                      const char *topage, const char *frompage,
+                      const char *userdata_key, apr_pool_t *pool)
+{
+  xlate_handle_node_t *old_node = NULL;
+
+  /* If we already have a handle, just return it. */
+  if (userdata_key)
+    {
+      if (xlate_handle_hash)
+        {
+          /* 1st level: global, static items */
+          if (userdata_key == SVN_UTF_NTOU_XLATE_HANDLE)
+            old_node = atomic_swap(&xlat_ntou_static_handle, NULL);
+          else if (userdata_key == SVN_UTF_UTON_XLATE_HANDLE)
+            old_node = atomic_swap(&xlat_uton_static_handle, NULL);
+
+          if (old_node && old_node->valid)
+            {
+              *ret = old_node;
+              return SVN_NO_ERROR;
+            }
+        }
+      else
+        {
+          void *p;
+          /* We fall back on a per-pool cache instead. */
+          apr_pool_userdata_get(&p, userdata_key, pool);
+          old_node = p;
+          /* Ensure that the handle is still valid. */
+          if (old_node && old_node->valid)
+            {
+              *ret = old_node;
+              return SVN_NO_ERROR;
+            }
+
+          return xlate_alloc_handle(ret, topage, frompage, pool);
+        }
     }
-#endif
 
-  return err;
+  SVN_MUTEX__WITH_LOCK(xlate_handle_mutex,
+                       get_xlate_handle_node_internal(ret,
+                                                      topage,
+                                                      frompage,
+                                                      userdata_key,
+                                                      pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Put back NODE into the xlate handle cache for use by other calls.
+   
+   Note: this function is not thread-safe. Call put_xlate_handle_node
+   instead. */
+static svn_error_t *
+put_xlate_handle_node_internal(xlate_handle_node_t *node,
+                               const char *userdata_key)
+{
+  xlate_handle_node_t **node_p = apr_hash_get(xlate_handle_hash,
+                                              userdata_key,
+                                              APR_HASH_KEY_STRING);
+  if (node_p == NULL)
+    {
+      userdata_key = apr_pstrdup(apr_hash_pool_get(xlate_handle_hash),
+                                  userdata_key);
+      node_p = apr_palloc(apr_hash_pool_get(xlate_handle_hash),
+                          sizeof(*node_p));
+      *node_p = NULL;
+      apr_hash_set(xlate_handle_hash, userdata_key,
+                    APR_HASH_KEY_STRING, node_p);
+    }
+  node->next = *node_p;
+  *node_p = node;
+  
+  return SVN_NO_ERROR;
 }
 
 /* Put back NODE into the xlate handle cache for use by other calls.
    If there is no global cache, store the handle in POOL.
-   Ignore errors related to locking/unlocking the mutex.
-   ### Mutex errors here are very weird. Should we handle them "correctly"
-   ### even if that complicates error handling in the routines below? */
-static void
+   Ignore errors related to locking/unlocking the mutex. */
+static svn_error_t *
 put_xlate_handle_node(xlate_handle_node_t *node,
                       const char *userdata_key,
                       apr_pool_t *pool)
 {
   assert(node->next == NULL);
   if (!userdata_key)
-    return;
+    return SVN_NO_ERROR;
 
   /* push previous global node to the hash */
   if (xlate_handle_hash)
     {
-      xlate_handle_node_t **node_p;
-
       /* 1st level: global, static items */
       if (userdata_key == SVN_UTF_NTOU_XLATE_HANDLE)
         node = atomic_swap(&xlat_ntou_static_handle, node);
       else if (userdata_key == SVN_UTF_UTON_XLATE_HANDLE)
         node = atomic_swap(&xlat_uton_static_handle, node);
       if (node == NULL)
-        return;
+        return SVN_NO_ERROR;
 
-#if APR_HAS_THREADS
-      if (apr_thread_mutex_lock(xlate_handle_mutex) != APR_SUCCESS)
-        SVN_ERR_MALFUNCTION_NO_RETURN();
-#endif
-      node_p = apr_hash_get(xlate_handle_hash, userdata_key,
-                            APR_HASH_KEY_STRING);
-      if (node_p == NULL)
-        {
-          userdata_key = apr_pstrdup(apr_hash_pool_get(xlate_handle_hash),
-                                     userdata_key);
-          node_p = apr_palloc(apr_hash_pool_get(xlate_handle_hash),
-                              sizeof(*node_p));
-          *node_p = NULL;
-          apr_hash_set(xlate_handle_hash, userdata_key,
-                       APR_HASH_KEY_STRING, node_p);
-        }
-      node->next = *node_p;
-      *node_p = node;
-#if APR_HAS_THREADS
-      if (apr_thread_mutex_unlock(xlate_handle_mutex) != APR_SUCCESS)
-        SVN_ERR_MALFUNCTION_NO_RETURN();
-#endif
+      SVN_MUTEX__WITH_LOCK(xlate_handle_mutex,
+                           put_xlate_handle_node_internal(node, 
+                                                          userdata_key));
     }
   else
     {
       /* Store it in the per-pool cache. */
       apr_pool_userdata_set(node, userdata_key, apr_pool_cleanup_null, pool);
     }
+    
+  return SVN_NO_ERROR;
 }
 
 /* Return the apr_xlate handle for converting native characters to UTF-8. */
@@ -720,9 +735,11 @@ svn_utf_stringbuf_to_utf8(svn_stringbuf_
         *dest = svn_stringbuf_dup(src, pool);
     }
 
-  put_xlate_handle_node(node, SVN_UTF_NTOU_XLATE_HANDLE, pool);
-
-  return err;
+  return svn_error_compose_create(err,
+                                  put_xlate_handle_node
+                                     (node, 
+                                      SVN_UTF_NTOU_XLATE_HANDLE,
+                                      pool));
 }
 
 
@@ -752,9 +769,11 @@ svn_utf_string_to_utf8(const svn_string_
         *dest = svn_string_dup(src, pool);
     }
 
-  put_xlate_handle_node(node, SVN_UTF_NTOU_XLATE_HANDLE, pool);
-
-  return err;
+  return svn_error_compose_create(err,
+                                  put_xlate_handle_node
+                                     (node, 
+                                      SVN_UTF_NTOU_XLATE_HANDLE,
+                                      pool));
 }
 
 
@@ -795,8 +814,11 @@ svn_utf_cstring_to_utf8(const char **des
 
   SVN_ERR(get_ntou_xlate_handle_node(&node, pool));
   err = convert_cstring(dest, src, node, pool);
-  put_xlate_handle_node(node, SVN_UTF_NTOU_XLATE_HANDLE, pool);
-  SVN_ERR(err);
+  SVN_ERR(svn_error_compose_create(err,
+                                   put_xlate_handle_node
+                                      (node, 
+                                       SVN_UTF_NTOU_XLATE_HANDLE,
+                                       pool)));
   return check_cstring_utf8(*dest, pool);
 }
 
@@ -815,8 +837,12 @@ svn_utf_cstring_to_utf8_ex2(const char *
   SVN_ERR(get_xlate_handle_node(&node, SVN_APR_UTF8_CHARSET, frompage,
                                 convset_key, pool));
   err = convert_cstring(dest, src, node, pool);
-  put_xlate_handle_node(node, convset_key, pool);
-  SVN_ERR(err);
+  SVN_ERR(svn_error_compose_create(err,
+                                   put_xlate_handle_node
+                                      (node, 
+                                       SVN_UTF_NTOU_XLATE_HANDLE,
+                                       pool)));
+
   return check_cstring_utf8(*dest, pool);
 }