You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/10/15 10:52:18 UTC

svn commit: r1532250 [21/37] - in /subversion/branches/cache-server: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ contrib/client-side/emacs/ contrib/hook-scripts/ contrib/server-side/fsfsfixer/ contrib/se...

Modified: subversion/branches/cache-server/subversion/libsvn_subr/cache-membuffer.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_subr/cache-membuffer.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_subr/cache-membuffer.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_subr/cache-membuffer.c Tue Oct 15 08:52:06 2013
@@ -31,6 +31,7 @@
 #include "svn_private_config.h"
 #include "cache.h"
 #include "svn_string.h"
+#include "svn_sorts.h"  /* get the MIN macro */
 #include "private/svn_dep_compat.h"
 #include "private/svn_mutex.h"
 #include "private/svn_pseudo_md5.h"
@@ -50,7 +51,8 @@
  * 2. A directory of cache entries. This is organized similar to CPU
  *    data caches: for every possible key, there is exactly one group
  *    of entries that may contain the header info for an item with
- *    that given key. The result is a GROUP_SIZE-way associative cache.
+ *    that given key.  The result is a GROUP_SIZE+-way associative cache
+ *    whose associativity can be dynamically increased.
  *
  * Only the start address of these two data parts are given as a native
  * pointer. All other references are expressed as offsets to these pointers.
@@ -96,6 +98,10 @@
  * about 50% of the content survives every 50% of the cache being re-written
  * with new entries. For details on the fine-tuning involved, see the
  * comments in ensure_data_insertable_l2().
+ * 
+ * Due to the randomized mapping of keys to entry groups, some groups may
+ * overflow.  In that case, there are spare groups that can be chained to
+ * an already used group to extend it.
  *
  * To limit the entry size and management overhead, not the actual item keys
  * but only their MD5-based hashes will be stored. This is reasonably safe
@@ -110,13 +116,6 @@
  * on their hash key.
  */
 
-/* A 16-way associative cache seems to be a good compromise between
- * performance (worst-case lookups) and efficiency-loss due to collisions.
- *
- * This value may be changed to any positive integer.
- */
-#define GROUP_SIZE 16
-
 /* For more efficient copy operations, let's align all data items properly.
  * Must be a power of 2.
  */
@@ -297,17 +296,18 @@ static svn_error_t* assert_equal_tags(co
 
 #define DEBUG_CACHE_MEMBUFFER_TAG tag,
 
-#define DEBUG_CACHE_MEMBUFFER_INIT_TAG                         \
-  entry_tag_t _tag;                                            \
-  entry_tag_t *tag = &_tag;                                    \
-  SVN_ERR(store_key_part(tag,                                  \
-                         cache->prefix,                        \
-                         cache->prefix_tail,                   \
-                         key,                                  \
-                         cache->key_len == APR_HASH_KEY_STRING \
-                             ? strlen((const char *) key)      \
-                             : cache->key_len,                 \
-                         cache->pool));
+#define DEBUG_CACHE_MEMBUFFER_INIT_TAG                           \
+  entry_tag_t _tag;                                              \
+  entry_tag_t *tag = &_tag;                                      \
+  if (key)                                                       \
+    SVN_ERR(store_key_part(tag,                                  \
+                           cache->prefix,                        \
+                           cache->prefix_tail,                   \
+                           key,                                  \
+                           cache->key_len == APR_HASH_KEY_STRING \
+                               ? strlen((const char *) key)      \
+                               : cache->key_len,                 \
+                           cache->pool));
 
 #else
 
@@ -335,10 +335,11 @@ typedef struct entry_t
    */
   apr_uint64_t offset;
 
-  /* Size of the serialized item data. May be 0.
+  /* Size of the serialized item data. May be 0.  The MAX_ITEM_SIZE macro
+   * above ensures that there will be no overflows.
    * Only valid for used entries.
    */
-  apr_size_t size;
+  apr_uint32_t size;
 
   /* Number of (read) hits for this entry. Will be reset upon write.
    * Only valid for used entries.
@@ -360,6 +361,10 @@ typedef struct entry_t
    */
   apr_uint32_t previous;
 
+  /* Priority of this entry.  This entry will not be replaced by lower-
+   * priority items.
+   */
+  apr_uint32_t priority;
 #ifdef SVN_DEBUG_CACHE_MEMBUFFER
   /* Remember type, content and key hashes.
    */
@@ -367,15 +372,60 @@ typedef struct entry_t
 #endif
 } entry_t;
 
-/* We group dictionary entries to make this GROUP-SIZE-way associative.
+/* Group header struct.
  */
-typedef struct entry_group_t
+typedef struct group_header_t
 {
   /* number of entries used [0 .. USED-1] */
   apr_uint32_t used;
 
+  /* next group in the chain or NO_INDEX for the last.
+   * For recycleable unused spare groups, this points to the next
+   * unused spare group */
+  apr_uint32_t next;
+
+  /* previously group in the chain or NO_INDEX for the first */
+  apr_uint32_t previous;
+
+  /* number of elements in the chain from start to here.
+   * >= 1 for used groups, 0 for unused spare groups */
+  apr_uint32_t chain_length;
+ 
+} group_header_t;
+
+/* The size of the group struct should be a power of two make sure it does
+ * not cross memory page boundaries.  Since we already access the cache
+ * randomly, having two page table lookups instead of one is bad.
+ */
+#define GROUP_BLOCK_SIZE 512
+
+/* A ~10-way associative cache seems to be a good compromise between
+ * performance (worst-case lookups) and efficiency-loss due to collisions.
+ *
+ * This value may be changed to any positive integer.
+ */
+#define GROUP_SIZE \
+          ((GROUP_BLOCK_SIZE - sizeof(group_header_t)) / sizeof(entry_t))
+
+/* Maximum number of groups in a chain, i.e. a cache index group can hold
+ * up to GROUP_SIZE * MAX_GROUP_CHAIN_LENGTH entries.
+ */
+#define MAX_GROUP_CHAIN_LENGTH 8
+
+/* We group dictionary entries to make this GROUP-SIZE-way associative.
+ */
+typedef struct entry_group_t
+{
+  /* group globals */
+  group_header_t header;
+  
+  /* padding and also room for future extensions */
+  char padding[GROUP_BLOCK_SIZE - sizeof(group_header_t)
+               - sizeof(entry_t) * GROUP_SIZE];
+
   /* the actual entries */
   entry_t entries[GROUP_SIZE];
+
 } entry_group_t;
 
 /* Per-cache level header structure.  Instances of this are members of
@@ -420,6 +470,49 @@ typedef struct cache_level_t
 
 /* The cache header structure.
  */
+typedef struct cache_level_t
+{
+  /* Number of cache segments. Must be a power of 2.
+     Please note that this structure represents only one such segment
+     and that all segments must / will report the same values here. */
+  apr_uint32_t segment_count;
+
+  /* The dictionary, GROUP_SIZE * (group_count + spare_group_count)
+   * entries long.  Never NULL.
+   */
+  entry_group_t *directory;
+
+  /* Flag array with group_count / GROUP_INIT_GRANULARITY _bit_ elements.
+   * Allows for efficiently marking groups as "not initialized".
+   */
+  unsigned char *group_initialized;
+
+  /* Size of dictionary in groups. Must be > 0.
+   */
+  apr_uint32_t group_count;
+
+  /* Total number of spare groups.
+   */
+  apr_uint32_t spare_group_count;
+
+  /* First recycleable spare group.
+   */
+  apr_uint32_t first_spare_group;
+
+  /* Maximum number of spare groups ever used.  I.e. group index
+   * group_count + max_spare_used is the first unused spare group
+   * if first_spare_group is NO_INDEX.
+   */
+  apr_uint32_t max_spare_used;
+
+  /* First offset in the caches DATA buffer that belongs to this level.
+   */
+  apr_uint64_t start_offset;
+
+} cache_level_t;
+
+/* The cache header structure.
+ */
 struct svn_membuffer_t
 {
   /* Number of cache segments. Must be a power of 2.
@@ -640,6 +733,132 @@ do {                                    
   SVN_ERR(unlock_cache(cache, (expr)));                         \
 } while (0)
 
+/* Returns 0 if the entry group identified by GROUP_INDEX in CACHE has not
+ * been initialized, yet. In that case, this group can not data. Otherwise,
+ * a non-zero value is returned.
+ */
+static APR_INLINE unsigned char
+is_group_initialized(svn_membuffer_t *cache, apr_uint32_t group_index)
+{
+  unsigned char flags
+    = cache->group_initialized[group_index / (8 * GROUP_INIT_GRANULARITY)];
+  unsigned char bit_mask
+    = (unsigned char)(1 << ((group_index / GROUP_INIT_GRANULARITY) % 8));
+
+  return flags & bit_mask;
+}
+
+/* Initializes the section of the directory in CACHE that contains
+ * the entry group identified by GROUP_INDEX. */
+static void
+initialize_group(svn_membuffer_t *cache, apr_uint32_t group_index)
+{
+  unsigned char bit_mask;
+  apr_uint32_t i;
+
+  /* range of groups to initialize due to GROUP_INIT_GRANULARITY */
+  apr_uint32_t first_index =
+      (group_index / GROUP_INIT_GRANULARITY) * GROUP_INIT_GRANULARITY;
+  apr_uint32_t last_index = first_index + GROUP_INIT_GRANULARITY;
+  if (last_index > cache->group_count)
+    last_index = cache->group_count;
+
+  for (i = first_index; i < last_index; ++i)
+    {
+      group_header_t *header = &cache->directory[i].header;
+      header->used = 0;
+      header->chain_length = 1;
+      header->next = NO_INDEX;
+      header->previous = NO_INDEX;
+    }
+
+  /* set the "initialized" bit for these groups */
+  bit_mask
+    = (unsigned char)(1 << ((group_index / GROUP_INIT_GRANULARITY) % 8));
+  cache->group_initialized[group_index / (8 * GROUP_INIT_GRANULARITY)]
+    |= bit_mask;
+}
+
+/* Return the next available spare group from CACHE and mark it as used.
+ * May return NULL.
+ */
+static entry_group_t *
+allocate_spare_group(svn_membuffer_t *cache)
+{
+  entry_group_t *group = NULL;
+
+  /* is there some ready-to-use group? */
+  if (cache->first_spare_group != NO_INDEX)
+    {
+      group = &cache->directory[cache->first_spare_group];
+      cache->first_spare_group = group->header.next;
+    }
+
+  /* any so far untouched spares available? */
+  else if (cache->max_spare_used < cache->spare_group_count)
+    {
+      apr_uint32_t group_index = cache->group_count + cache->max_spare_used;
+      ++cache->max_spare_used;
+
+      if (!is_group_initialized(cache, group_index))
+        initialize_group(cache, group_index);
+
+      group = &cache->directory[group_index];
+    }
+
+  /* spare groups must be empty */
+  assert(!group || !group->header.used);
+  return group;
+}
+
+/* Mark previously allocated spare group GROUP in CACHE as "unused".
+ */
+static void
+free_spare_group(svn_membuffer_t *cache,
+                 entry_group_t *group)
+{
+  assert(group->header.used == 0);
+  assert(group->header.previous != NO_INDEX);
+  assert(group - cache->directory >= cache->group_count);
+
+  /* unchain */
+  cache->directory[group->header.previous].header.next = NO_INDEX;
+  group->header.chain_length = 0;
+  group->header.previous = NO_INDEX;
+
+  /* add to chain of spares */
+  group->header.next = cache->first_spare_group;
+  cache->first_spare_group = group - cache->directory;
+}
+
+/* Follow the group chain from GROUP in CACHE to its end and return the last
+ * group.  May return GROUP.
+ */
+static entry_group_t *
+last_group_in_chain(svn_membuffer_t *cache,
+                    entry_group_t *group)
+{
+  while (group->header.next != NO_INDEX)
+    group = &cache->directory[group->header.next];
+
+  return group;
+}
+
+/* Return the CHAIN_INDEX-th element in the group chain starting from group
+ * START_GROUP_INDEX in CACHE.
+ */
+static entry_group_t *
+get_group(svn_membuffer_t *cache,
+          apr_uint32_t start_group_index,
+          apr_uint32_t chain_index)
+{
+  entry_group_t *group = &cache->directory[start_group_index];
+  for (; chain_index; --chain_index)
+    group = &cache->directory[group->header.next];
+
+  return group;
+}
+
 /* Resolve a dictionary entry reference, i.e. return the entry
  * for the given IDX.
  */
@@ -761,13 +980,14 @@ drop_entry(svn_membuffer_t *cache, entry
    */
   apr_uint32_t idx = get_index(cache, entry);
   apr_uint32_t group_index = idx / GROUP_SIZE;
-  entry_group_t *group = &cache->directory[group_index];
-  apr_uint32_t last_in_group = group_index * GROUP_SIZE + group->used - 1;
+  entry_group_t *last_group
+    = last_group_in_chain(cache, &cache->directory[group_index]);
+  apr_uint32_t last_in_group
+    = (last_group - cache->directory) * GROUP_SIZE
+    + last_group->header.used - 1;
   cache_level_t *level = get_cache_level(cache, entry);
 
-  /* Only valid to be called for used entries.
-   */
-  assert(idx <= last_in_group);
+  cache_level_t *level = get_cache_level(cache, entry);
 
   /* update global cache usage counters
    */
@@ -801,16 +1021,16 @@ drop_entry(svn_membuffer_t *cache, entry
   /* unlink it from the chain of used entries
    */
   unchain_entry(cache, level, entry, idx);
-  
+
   /* Move last entry into hole (if the removed one is not the last used).
    * We need to do this since all used entries are at the beginning of
    * the group's entries array.
    */
-  if (idx < last_in_group)
+  if (idx != last_in_group)
     {
       /* copy the last used entry to the removed entry's index
        */
-      *entry = group->entries[group->used-1];
+      *entry = last_group->entries[last_group->header.used-1];
 
       /* this ENTRY may belong to a different cache level than the entry
        * we have just removed */
@@ -834,7 +1054,12 @@ drop_entry(svn_membuffer_t *cache, entry
 
   /* Update the number of used entries.
    */
-  group->used--;
+  last_group->header.used--;
+
+  /* Release the last group in the chain if it is a spare group
+   */
+  if (!last_group->header.used && last_group->header.previous != NO_INDEX)
+    free_spare_group(cache, last_group);
 }
 
 /* Insert ENTRY into the chain of used dictionary entries. The entry's
@@ -855,7 +1080,7 @@ insert_entry(svn_membuffer_t *cache, ent
    * It must also be the first unused entry in the group.
    */
   assert(entry->offset == level->current_data);
-  assert(idx == group_index * GROUP_SIZE + group->used);
+  assert(idx == group_index * GROUP_SIZE + group->header.used);
   level->current_data = ALIGN_VALUE(entry->offset + entry->size);
 
   /* update usage counters
@@ -863,7 +1088,7 @@ insert_entry(svn_membuffer_t *cache, ent
   cache->used_entries++;
   cache->data_used += entry->size;
   entry->hit_count = 0;
-  group->used++;
+  group->header.used++;
 
   /* update entry chain
    */
@@ -888,8 +1113,9 @@ get_group_index(svn_membuffer_t **cache,
    * Since key may not be well-distributed, pre-fold it to a smaller but
    * "denser" ranger.  The divisors are primes larger than the largest
    * counts. */
-  *cache = &segment0[(key[1] % 2809637ull) & (segment0->segment_count - 1)];
-  return (key[0] % 5030895599ull) % segment0->group_count;
+  *cache = &segment0[(key[1] % APR_UINT64_C(2809637))
+         & (segment0->segment_count - 1)];
+  return (key[0] % APR_UINT64_C(5030895599)) % segment0->group_count;
 }
 
 /* Reduce the hit count of ENTRY and update the accumulated hit info
@@ -904,46 +1130,6 @@ let_entry_age(svn_membuffer_t *cache, en
   entry->hit_count -= hits_removed;
 }
 
-/* Returns 0 if the entry group identified by GROUP_INDEX in CACHE has not
- * been initialized, yet. In that case, this group can not data. Otherwise,
- * a non-zero value is returned.
- */
-static APR_INLINE unsigned char
-is_group_initialized(svn_membuffer_t *cache, apr_uint32_t group_index)
-{
-  unsigned char flags
-    = cache->group_initialized[group_index / (8 * GROUP_INIT_GRANULARITY)];
-  unsigned char bit_mask
-    = (unsigned char)(1 << ((group_index / GROUP_INIT_GRANULARITY) % 8));
-
-  return flags & bit_mask;
-}
-
-/* Initializes the section of the directory in CACHE that contains
- * the entry group identified by GROUP_INDEX. */
-static void
-initialize_group(svn_membuffer_t *cache, apr_uint32_t group_index)
-{
-  unsigned char bit_mask;
-  apr_uint32_t i;
-
-  /* range of groups to initialize due to GROUP_INIT_GRANULARITY */
-  apr_uint32_t first_index =
-      (group_index / GROUP_INIT_GRANULARITY) * GROUP_INIT_GRANULARITY;
-  apr_uint32_t last_index = first_index + GROUP_INIT_GRANULARITY;
-  if (last_index > cache->group_count)
-    last_index = cache->group_count;
-
-  for (i = first_index; i < last_index; ++i)
-    cache->directory[i].used = 0;
-
-  /* set the "initialized" bit for these groups */
-  bit_mask
-    = (unsigned char)(1 << ((group_index / GROUP_INIT_GRANULARITY) % 8));
-  cache->group_initialized[group_index / (8 * GROUP_INIT_GRANULARITY)]
-    |= bit_mask;
-}
-
 /* Given the GROUP_INDEX that shall contain an entry with the hash key
  * TO_FIND, find that entry in the specified group.
  *
@@ -989,43 +1175,98 @@ find_entry(svn_membuffer_t *cache,
 
   /* try to find the matching entry
    */
-  for (i = 0; i < group->used; ++i)
-    if (   to_find[0] == group->entries[i].key[0]
-        && to_find[1] == group->entries[i].key[1])
-      {
-        /* found it
-         */
-        entry = &group->entries[i];
-        if (find_empty)
-          drop_entry(cache, entry);
-        else
-          return entry;
-      }
+  while (1)
+    {
+      for (i = 0; i < group->header.used; ++i)
+        if (   to_find[0] == group->entries[i].key[0]
+            && to_find[1] == group->entries[i].key[1])
+          {
+            /* found it
+             */
+            entry = &group->entries[i];
+            if (!find_empty)
+              return entry;
+
+            /* need to empty that entry */
+            drop_entry(cache, entry);
+            if (group->header.used == GROUP_SIZE)
+              group = last_group_in_chain(cache, group);
+            else if (group->header.chain_length == 0)
+              group = last_group_in_chain(cache,
+                                          &cache->directory[group_index]);
+
+            break;
+          }
+
+      /* end of chain? */
+      if (group->header.next == NO_INDEX)
+        break;
+
+      /* only full groups may chain */
+      assert(group->header.used == GROUP_SIZE);
+      group = &cache->directory[group->header.next];
+    }
 
   /* None found. Are we looking for a free entry?
    */
   if (find_empty)
     {
-      /* if there is no empty entry, delete the oldest entry
+      /* There is no empty entry in the chain, try chaining a spare group.
        */
-      if (group->used == GROUP_SIZE)
+      if (   group->header.used == GROUP_SIZE
+          && group->header.chain_length < MAX_GROUP_CHAIN_LENGTH)
+        {
+          entry_group_t *new_group = allocate_spare_group(cache);
+          if (new_group)
+            {
+              /* chain groups
+               */
+              new_group->header.chain_length = group->header.chain_length + 1;
+              new_group->header.previous = group - cache->directory;
+              new_group->header.next = NO_INDEX;
+              group->header.next = new_group - cache->directory;
+              group = new_group;
+            }
+        }
+
+      /* if GROUP is still filled, we need to remove a random entry */
+      if (group->header.used == GROUP_SIZE)
         {
           /* every entry gets the same chance of being removed.
            * Otherwise, we free the first entry, fill it and
            * remove it again on the next occasion without considering
            * the other entries in this group.
+           *
+           * We hit only one random group instead of processing all
+           * groups in the chain.
            */
-          entry = &group->entries[rand() % GROUP_SIZE];
-          for (i = 1; i < GROUP_SIZE; ++i)
-            if (entry->hit_count > group->entries[i].hit_count)
-              entry = &group->entries[i];
+          cache_level_t *entry_level;
+          int to_remove = rand() % (GROUP_SIZE * group->header.chain_length);
+          entry_group_t *to_shrink
+            = get_group(cache, group_index, to_remove / GROUP_SIZE);
+
+          entry = &to_shrink->entries[to_remove % GROUP_SIZE];
+          entry_level = get_cache_level(cache, entry);
+          for (i = 0; i < GROUP_SIZE; ++i)
+            {
+              /* keep L1 entries whenever possible */
+
+              cache_level_t *level
+                = get_cache_level(cache, &to_shrink->entries[i]);
+              if (   (level != entry_level && entry_level == &cache->l1)
+                  || (entry->hit_count > to_shrink->entries[i].hit_count))
+                {
+                  entry_level = level;
+                  entry = &to_shrink->entries[i];
+                }
+            }
 
           /* for the entries that don't have been removed,
            * reduce their hit counts to put them at a relative
            * disadvantage the next time.
            */
           for (i = 0; i < GROUP_SIZE; ++i)
-            if (entry != &group->entries[i])
+            if (entry != &to_shrink->entries[i])
               let_entry_age(cache, entry);
 
           drop_entry(cache, entry);
@@ -1033,7 +1274,7 @@ find_entry(svn_membuffer_t *cache,
 
       /* initialize entry for the new key
        */
-      entry = &group->entries[group->used];
+      entry = &group->entries[group->header.used];
       entry->key[0] = to_find[0];
       entry->key[1] = to_find[1];
     }
@@ -1089,6 +1330,7 @@ promote_entry(svn_membuffer_t *cache, en
   apr_uint32_t idx = get_index(cache, entry);
   apr_size_t size = ALIGN_VALUE(entry->size);
   assert(get_cache_level(cache, entry) == &cache->l1);
+  assert(idx == cache->l1.next);
 
   /* copy item from the current location in L1 to the start of L2's
    * insertion window */
@@ -1119,19 +1361,25 @@ promote_entry(svn_membuffer_t *cache, en
  */
 static svn_boolean_t
 ensure_data_insertable_l2(svn_membuffer_t *cache,
-                          entry_t *to_fit_in,
-                          apr_uint32_t idx)
+                          entry_t *to_fit_in)
 {
   entry_t *entry;
   apr_uint64_t average_hit_value;
   apr_uint64_t threshold;
 
+  /* accumulated size of the entries that have been removed to make
+   * room for the new one.
+   */
+  apr_size_t moved_size = 0;
+
+  /* count the number of entries that got moved.  A single large entry
+   * being moved is not enough to reject an insertion.
+   */
+  apr_size_t moved_count = 0;
+
   /* accumulated "worth" of items dropped so far */
   apr_size_t drop_hits = 0;
 
-  /* verify parameters */
-  assert(idx == get_index(cache, to_fit_in));
-
   /* This loop will eventually terminate because every cache entry
    * would get dropped eventually:
    * - hit counts become 0 after the got kept for 32 full scans
@@ -1157,10 +1405,16 @@ ensure_data_insertable_l2(svn_membuffer_
       if (end >= to_fit_in->size + cache->l2.current_data)
         return TRUE;
 
+      /* Don't be too eager to cache data.  If a lot of data has been 
+       * moved around, the current item has probably a relatively low
+       * priority.  So, give up after some time.
+       */
+      if (moved_size > 8 * to_fit_in->size && moved_count > 3)
+        return FALSE;
+
       /* if the net worth (in hits) of items removed is already larger
        * than what we want to insert, reject TO_FIT_IN because it still
-       * does not fit in.
-       */
+       * does not fit in. */
       if (drop_hits > to_fit_in->hit_count)
         return FALSE;
 
@@ -1178,8 +1432,15 @@ ensure_data_insertable_l2(svn_membuffer_
         }
       else
         {
+          svn_boolean_t keep;
           entry = get_entry(cache, cache->l2.next);
 
+          /* Reject insertion for entries with low priority, if the current
+           * entry has seen recent hits. */
+          if (   entry->hit_count
+              && to_fit_in->priority < SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY)
+            return FALSE;
+
           /* Keep entries that are very small. Those are likely to be data
            * headers or similar management structures. So, they are probably
            * important while not occupying much space.
@@ -1188,6 +1449,55 @@ ensure_data_insertable_l2(svn_membuffer_
           if (   (apr_uint64_t)entry->size * cache->used_entries
                < cache->data_used / 8)
             {
+              keep = TRUE;
+            }
+          else if (   entry->priority < SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY
+                   && to_fit_in->priority > entry->priority)
+            {
+              /* Be quick to evict low-priority entries if the one to insert
+               * is of higher priority.
+               */
+              keep = FALSE;
+            }
+          else if (to_fit_in->priority != entry->priority)
+            {
+              /* Not the same priority but the lower prio side is not a
+               * clear loser either (already checked those cases above).
+               * Keep the current entry if it has seen more hits recently
+               * or is smaller than the one to insert - both relative to
+               * their respective priority.
+               */
+              keep = to_fit_in->hit_count * to_fit_in->priority
+                   < entry->hit_count * entry->priority
+                  || to_fit_in->size * to_fit_in->priority
+                   < entry->size * entry->priority;
+            }
+          else if (cache->hit_count > cache->used_entries)
+            {
+              /* Roll the dice and determine a threshold somewhere
+               * from 0 up to 2 times the average hit count.
+               */
+              average_hit_value = cache->hit_count / cache->used_entries;
+              threshold = (average_hit_value+1) * (rand() % 4096) / 2048;
+
+              keep = entry->hit_count > threshold;
+            }
+          else
+            {
+              /* general hit count is low. Keep everything that got hit
+               * at all and assign some 50% survival chance to everything
+               * else.
+               */
+              keep = rand() & 1;
+            }
+
+          /* keepers or destroyers? */
+          if (keep)
+            {
+              /* Moving entries around is not for free -> track costs. */
+              moved_size += entry->size;
+              moved_count++;
+
               move_entry(cache, entry);
             }
           else if (cache->l2.next / GROUP_SIZE == idx / GROUP_SIZE)
@@ -1202,42 +1512,80 @@ ensure_data_insertable_l2(svn_membuffer_
             }
           else
             {
-              svn_boolean_t keep;
+              /* Drop the entry from the end of the insertion window, if it
+               * has been hit less than the threshold. Otherwise, keep it and
+               * move the insertion window one entry further.
+               */
+              drop_hits += entry->hit_count;
+              drop_entry(cache, entry);
+            }
+        }
+    }
 
-              if (cache->hit_count > cache->used_entries)
-                {
-                  /* Roll the dice and determine a threshold somewhere from
-                   * 0 up to 2 times the average hit count.
-                   */
-                  average_hit_value = cache->hit_count / cache->used_entries;
-                  threshold = (average_hit_value+1) * (rand() % 4096) / 2048;
+  /* This will never be reached. But if it was, "can't insert" was the
+   * right answer. */
+}
 
-                  keep = entry->hit_count >= threshold;
-                }
-              else
-                {
-                  /* general hit count is low. Keep everything that got
-                   * hit at all and assign some 50% survival chance to
-                   * everything else.
-                   */
-                  keep = (entry->hit_count > 0) || (rand() & 1);
-                }
+/* This function implements the cache insertion / eviction strategy for L1.
+ *
+ * If necessary, enlarge the insertion window of CACHE->L1 by promoting
+ * entries to L2 until it is at least SIZE bytes long.
+ *
+ * Return TRUE if enough room could be found or made.  A FALSE result
+ * indicates that the respective item shall not be added because it is
+ * too large.
+ */
+static svn_boolean_t
+ensure_data_insertable_l1(svn_membuffer_t *cache, apr_size_t size)
+{
+  /* Guarantees that the while loop will terminate. */
+  if (size > cache->l1.size)
+    return FALSE;
+
+  /* This loop will eventually terminate because every cache entry
+   * would get dropped eventually.
+   */
+  while (1)
+    {
+      /* first offset behind the insertion window
+       */
+      apr_uint32_t entry_index = cache->l1.next;
+      entry_t *entry = get_entry(cache, entry_index);
+      apr_uint64_t end = cache->l1.next == NO_INDEX
+                       ? cache->l1.start_offset + cache->l1.size
+                       : entry->offset;
+
+      /* leave function as soon as the insertion window is large enough
+       */
+      if (end >= size + cache->l1.current_data)
+        return TRUE;
 
-              /* keepers or destroyers? */
+      /* Enlarge the insertion window
+       */
+      if (cache->l1.next == NO_INDEX)
+        {
+          /* We reached the end of the data buffer; restart at the beginning.
+           * Due to the randomized nature of our LFU implementation, very
+           * large data items may require multiple passes. Therefore, SIZE
+           * should be restricted to significantly less than data_size.
+           */
+          cache->l1.current_data = cache->l1.start_offset;
+          cache->l1.next = cache->l1.first;
+        }
+      else
+        {
+          /* Remove the entry from the end of insertion window and promote
+           * it to L2, if it is important enough.
+           */
+          svn_boolean_t keep = ensure_data_insertable_l2(cache, entry);
+
+          /* We might have touched the group that contains ENTRY. Recheck. */
+          if (entry_index == cache->l1.next)
+            {
               if (keep)
-                {
-                 /* Keep ENTRY and move the insertion window.
-                  */
-                  move_entry(cache, entry);
-                }
+                promote_entry(cache, entry);
               else
-                {
-                 /* Drop the entry from the end of the insertion window,
-                  * because it had been hit less than the threshold.
-                  */
-                  drop_hits += entry->hit_count;
-                  drop_entry(cache, entry);
-                }
+                drop_entry(cache, entry);
             }
         }
     }
@@ -1345,6 +1693,8 @@ svn_cache__membuffer_cache_create(svn_me
 
   apr_uint32_t seg;
   apr_uint32_t group_count;
+  apr_uint32_t main_group_count;
+  apr_uint32_t spare_group_count;
   apr_uint32_t group_init_size;
   apr_uint64_t data_size;
   apr_uint64_t max_entry_size;
@@ -1419,8 +1769,8 @@ svn_cache__membuffer_cache_create(svn_me
    */
   if (directory_size > total_size - sizeof(entry_group_t))
     directory_size = total_size - sizeof(entry_group_t);
-  if (directory_size < sizeof(entry_group_t))
-    directory_size = sizeof(entry_group_t);
+  if (directory_size < 2 * sizeof(entry_group_t))
+    directory_size = 2 * sizeof(entry_group_t);
 
   /* limit the data size to what we can address.
    * Note that this cannot overflow since all values are of size_t.
@@ -1448,6 +1798,11 @@ svn_cache__membuffer_cache_create(svn_me
               ? (APR_UINT32_MAX / GROUP_SIZE) - 1
               : (apr_uint32_t)(directory_size / sizeof(entry_group_t));
 
+  /* set some of the index directory aside as over-flow (spare) buffers */
+  spare_group_count = MAX(group_count / 4, 1);
+  main_group_count = group_count - spare_group_count;
+  assert(spare_group_count > 0 && main_group_count > 0);
+
   group_init_size = 1 + group_count / (8 * GROUP_INIT_GRANULARITY);
   for (seg = 0; seg < segment_count; ++seg)
     {
@@ -1455,7 +1810,11 @@ svn_cache__membuffer_cache_create(svn_me
        */
       c[seg].segment_count = (apr_uint32_t)segment_count;
 
-      c[seg].group_count = group_count;
+      c[seg].group_count = main_group_count;
+      c[seg].spare_group_count = spare_group_count;
+      c[seg].first_spare_group = NO_INDEX;
+      c[seg].max_spare_used = 0;
+
       c[seg].directory = apr_pcalloc(pool,
                                      group_count * sizeof(entry_group_t));
 
@@ -1498,7 +1857,7 @@ svn_cache__membuffer_cache_create(svn_me
         {
           /* We are OOM. There is no need to proceed with "half a cache".
            */
-          return svn_error_wrap_apr(APR_ENOMEM, _("OOM"));
+          return svn_error_wrap_apr(APR_ENOMEM, "OOM");
         }
 
 #if APR_HAS_THREADS
@@ -1580,6 +1939,7 @@ membuffer_cache_set_internal(svn_membuff
                              apr_uint32_t group_index,
                              char *buffer,
                              apr_size_t size,
+                             apr_uint32_t priority,
                              DEBUG_CACHE_MEMBUFFER_TAG_ARG
                              apr_pool_t *scratch_pool)
 {
@@ -1592,6 +1952,7 @@ membuffer_cache_set_internal(svn_membuff
     {
       cache->data_used += size - entry->size;
       entry->size = size;
+      entry->priority = priority;
 
 #ifdef SVN_DEBUG_CACHE_MEMBUFFER
 
@@ -1622,6 +1983,7 @@ membuffer_cache_set_internal(svn_membuff
       entry = find_entry(cache, group_index, to_find, TRUE);
       entry->size = size;
       entry->offset = cache->l1.current_data;
+      entry->priority = priority;
 
 #ifdef SVN_DEBUG_CACHE_MEMBUFFER
 
@@ -1671,6 +2033,7 @@ membuffer_cache_set(svn_membuffer_t *cac
                     entry_key_t key,
                     void *item,
                     svn_cache__serialize_func_t serializer,
+                    apr_uint32_t priority,
                     DEBUG_CACHE_MEMBUFFER_TAG_ARG
                     apr_pool_t *scratch_pool)
 {
@@ -1695,6 +2058,7 @@ membuffer_cache_set(svn_membuffer_t *cac
                                                group_index,
                                                buffer,
                                                size,
+                                               priority,
                                                DEBUG_CACHE_MEMBUFFER_TAG
                                                scratch_pool));
   return SVN_NO_ERROR;
@@ -1806,6 +2170,62 @@ membuffer_cache_get(svn_membuffer_t *cac
 }
 
 /* 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, *FOUND
+ * will be FALSE and TRUE otherwise.
+ */
+static svn_error_t *
+membuffer_cache_has_key_internal(svn_membuffer_t *cache,
+                                 apr_uint32_t group_index,
+                                 entry_key_t to_find,
+                                 svn_boolean_t *found)
+{
+  entry_t *entry = find_entry(cache, group_index, to_find, FALSE);
+  if (entry)
+    {
+      /* This is often happen in "block read" where most data is already
+         in L2 and only a few previously evicted items are added to L1
+         again.  While items in L1 are well protected for a while, L2
+         items may get evicted soon.  Thus, mark all them as "hit" to give
+         them a higher chance for survival. */
+      entry->hit_count++;
+      cache->hit_count++;
+      cache->total_hits++;
+
+      *found = TRUE;
+    }
+  else
+    {
+      *found = FALSE;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Look for an entry identified by KEY.  If no item has been stored
+ * for KEY, *FOUND will be set to FALSE and TRUE otherwise.
+ */
+/* Implements svn_cache__has_key for membuffer caches.
+ */
+static svn_error_t *
+membuffer_cache_has_key(svn_membuffer_t *cache,
+                        entry_key_t key,
+                        svn_boolean_t *found)
+{
+  /* find the entry group that will hold the key.
+   */
+  apr_uint32_t group_index = get_group_index(&cache, key);
+  cache->total_reads++;
+
+  WITH_READ_LOCK(cache,
+                 membuffer_cache_has_key_internal(cache,
+                                                  group_index,
+                                                  key,
+                                                  found));
+
+  return SVN_NO_ERROR;
+}
+
+/* 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.
  *
@@ -2095,6 +2515,9 @@ typedef struct svn_membuffer_cache_t
    */
   apr_ssize_t key_len;
 
+  /* priority class for all items written through this interface */
+  apr_uint32_t priority;
+  
   /* Temporary buffer containing the hash key for the current access
    */
   entry_key_t combined_key;
@@ -2245,8 +2668,7 @@ combine_key(svn_membuffer_cache_t *cache
    * collisions.  So, we limit ourselves to xor and permutations. */
   data[1] = (data[1] << 27) | (data[1] >> 37);
   data[1] ^= data[0] & 0xffff;
-  data[0] ^= data[1] & 0xffffffffffff0000ull;
-  data[0] = (data[0] << 43) | (data[0] >> 21);
+  data[0] ^= data[1] & APR_UINT64_C(0xffffffffffff0000);
 
   /* combine with this cache's namespace */
   cache->combined_key[0] = data[0] ^ cache->prefix[0];
@@ -2290,6 +2712,39 @@ svn_membuffer_cache_get(void **value_p,
 
   /* return result */
   *found = *value_p != NULL;
+
+  return SVN_NO_ERROR;
+}
+
+/* Implement svn_cache__vtable_t.has_key (not thread-safe)
+ */
+static svn_error_t *
+svn_membuffer_cache_has_key(svn_boolean_t *found,
+                            void *cache_void,
+                            const void *key,
+                            apr_pool_t *scratch_pool)
+{
+  svn_membuffer_cache_t *cache = cache_void;
+
+  /* special case */
+  if (key == NULL)
+    {
+      *found = FALSE;
+
+      return SVN_NO_ERROR;
+    }
+
+  /* construct the full, i.e. globally unique, key by adding
+   * this cache instances' prefix
+   */
+  combine_key(cache, key, cache->key_len);
+
+  /* Look the item up. */
+  SVN_ERR(membuffer_cache_has_key(cache->membuffer,
+                                  cache->combined_key,
+                                  found));
+
+  /* return result */
   return SVN_NO_ERROR;
 }
 
@@ -2331,6 +2786,7 @@ svn_membuffer_cache_set(void *cache_void
                              cache->combined_key,
                              value,
                              cache->serializer,
+                             cache->priority,
                              DEBUG_CACHE_MEMBUFFER_TAG
                              cache->pool);
 }
@@ -2424,12 +2880,16 @@ svn_membuffer_cache_is_cachable(void *ca
   return size <= cache->membuffer->max_entry_size;
 }
 
-/* Add statistics of SEGMENT to INFO.
+/* Add statistics of SEGMENT to INFO.  If INCLUDE_HISTOGRAM is TRUE,
+ * accumulate index bucket fill levels in INFO->HISTOGRAM.
  */
 static svn_error_t *
 svn_membuffer_get_segment_info(svn_membuffer_t *segment,
-                               svn_cache__info_t *info)
+                               svn_cache__info_t *info,
+                               svn_boolean_t include_histogram)
 {
+  apr_size_t i;
+
   info->data_size += segment->l1.size + segment->l2.size;
   info->used_size += segment->data_used;
   info->total_size += segment->l1.size + segment->l2.size +
@@ -2438,6 +2898,17 @@ svn_membuffer_get_segment_info(svn_membu
   info->used_entries += segment->used_entries;
   info->total_entries += segment->group_count * GROUP_SIZE;
 
+  if (include_histogram)
+    for (i = 0; i < segment->group_count; ++i)
+      {
+        entry_group_t *chain_end
+          = last_group_in_chain(segment, &segment->directory[i]);
+        apr_size_t use
+          = MIN(chain_end->header.used,
+                sizeof(info->histogram) / sizeof(info->histogram[0]) - 1);
+        info->histogram[use]++;
+      }
+
   return SVN_NO_ERROR;
 }
 
@@ -2459,18 +2930,11 @@ svn_membuffer_cache_get_info(void *cache
 
   /* collect info from shared cache back-end */
 
-  info->data_size = 0;
-  info->used_size = 0;
-  info->total_size = 0;
-
-  info->used_entries = 0;
-  info->total_entries = 0;
-
   for (i = 0; i < cache->membuffer->segment_count; ++i)
     {
       svn_membuffer_t *segment = cache->membuffer + i;
       WITH_READ_LOCK(segment,
-                     svn_membuffer_get_segment_info(segment, info));
+                     svn_membuffer_get_segment_info(segment, info, FALSE));
     }
 
   return SVN_NO_ERROR;
@@ -2481,6 +2945,7 @@ svn_membuffer_cache_get_info(void *cache
  */
 static svn_cache__vtable_t membuffer_cache_vtable = {
   svn_membuffer_cache_get,
+  svn_membuffer_cache_has_key,
   svn_membuffer_cache_set,
   svn_membuffer_cache_iter,
   svn_membuffer_cache_is_cachable,
@@ -2509,6 +2974,24 @@ svn_membuffer_cache_get_synced(void **va
   return SVN_NO_ERROR;
 }
 
+/* Implement svn_cache__vtable_t.has_key and serialize all cache access.
+ */
+static svn_error_t *
+svn_membuffer_cache_has_key_synced(svn_boolean_t *found,
+                                   void *cache_void,
+                                   const void *key,
+                                   apr_pool_t *result_pool)
+{
+  svn_membuffer_cache_t *cache = cache_void;
+  SVN_MUTEX__WITH_LOCK(cache->mutex,
+                       svn_membuffer_cache_has_key(found,
+                                                   cache_void,
+                                                   key,
+                                                   result_pool));
+
+  return SVN_NO_ERROR;
+}
+
 /* Implement svn_cache__vtable_t.set and serialize all cache access.
  */
 static svn_error_t *
@@ -2575,6 +3058,7 @@ svn_membuffer_cache_set_partial_synced(v
  */
 static svn_cache__vtable_t membuffer_cache_synced_vtable = {
   svn_membuffer_cache_get_synced,
+  svn_membuffer_cache_has_key_synced,
   svn_membuffer_cache_set_synced,
   svn_membuffer_cache_iter,               /* no sync required */
   svn_membuffer_cache_is_cachable,        /* no sync required */
@@ -2629,6 +3113,7 @@ svn_cache__create_membuffer_cache(svn_ca
                                   svn_cache__deserialize_func_t deserializer,
                                   apr_ssize_t klen,
                                   const char *prefix,
+                                  apr_uint32_t priority,
                                   svn_boolean_t thread_safe,
                                   apr_pool_t *pool)
 {
@@ -2649,6 +3134,7 @@ svn_cache__create_membuffer_cache(svn_ca
                       ? deserializer
                       : deserialize_svn_stringbuf;
   cache->full_prefix = apr_pstrdup(pool, prefix);
+  cache->priority = priority;
   cache->key_len = klen;
   cache->pool = svn_pool_create(pool);
   cache->alloc_counter = 0;
@@ -2698,3 +3184,37 @@ svn_cache__create_membuffer_cache(svn_ca
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+svn_membuffer_get_global_segment_info(svn_membuffer_t *segment,
+                                      svn_cache__info_t *info)
+{
+  info->gets += segment->total_reads;
+  info->sets += segment->total_writes;
+  info->hits += segment->total_hits;
+
+  WITH_READ_LOCK(segment,
+                  svn_membuffer_get_segment_info(segment, info, TRUE));
+
+  return SVN_NO_ERROR;
+}
+
+svn_cache__info_t *
+svn_cache__membuffer_get_global_info(apr_pool_t *pool)
+{
+  apr_uint32_t i;
+
+  svn_membuffer_t *membuffer = svn_cache__get_global_membuffer_cache();
+  svn_cache__info_t *info = apr_pcalloc(pool, sizeof(*info));
+
+  /* cache front-end specific data */
+
+  info->id = "membuffer globals";
+
+  /* collect info from shared cache back-end */
+
+  for (i = 0; i < membuffer->segment_count; ++i)
+    svn_error_clear(svn_membuffer_get_global_segment_info(membuffer + i,
+                                                          info));
+
+  return info;
+}

Modified: subversion/branches/cache-server/subversion/libsvn_subr/cache-memcache.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_subr/cache-memcache.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_subr/cache-memcache.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_subr/cache-memcache.c Tue Oct 15 08:52:06 2013
@@ -213,6 +213,26 @@ memcache_get(void **value_p,
   return SVN_NO_ERROR;
 }
 
+/* Implement vtable.has_key in terms of the getter.
+ */
+static svn_error_t *
+memcache_has_key(svn_boolean_t *found,
+                 void *cache_void,
+                 const void *key,
+                 apr_pool_t *scratch_pool)
+{
+  char *data;
+  apr_size_t data_len;
+  SVN_ERR(memcache_internal_get(&data,
+                                &data_len,
+                                found,
+                                cache_void,
+                                key,
+                                scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
 /* Core functionality of our setter functions: store LENGH bytes of DATA
  * to be identified by KEY in the memcached given by CACHE_VOID. Use POOL
  * for temporary allocations.
@@ -366,17 +386,12 @@ memcache_get_info(void *cache_void,
 
   /* we don't have any memory allocation info */
 
-  info->used_size = 0;
-  info->total_size = 0;
-  info->data_size = 0;
-  info->used_entries = 0;
-  info->total_entries = 0;
-
   return SVN_NO_ERROR;
 }
 
 static svn_cache__vtable_t memcache_vtable = {
   memcache_get,
+  memcache_has_key,
   memcache_set,
   memcache_iter,
   memcache_is_cachable,

Modified: subversion/branches/cache-server/subversion/libsvn_subr/cache.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_subr/cache.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_subr/cache.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_subr/cache.c Tue Oct 15 08:52:06 2013
@@ -96,6 +96,21 @@ svn_cache__get(void **value_p,
 }
 
 svn_error_t *
+svn_cache__has_key(svn_boolean_t *found,
+                   svn_cache__t *cache,
+                   const void *key,
+                   apr_pool_t *scratch_pool)
+{
+  *found = FALSE;
+  return handle_error(cache,
+                      (cache->vtable->has_key)(found,
+                                               cache->cache_internal,
+                                               key,
+                                               scratch_pool),
+                      scratch_pool);
+}
+
+svn_error_t *
 svn_cache__set(svn_cache__t *cache,
                const void *key,
                void *value,
@@ -192,6 +207,7 @@ svn_cache__get_info(svn_cache__t *cache,
 {
   /* write general statistics */
 
+  memset(info, 0, sizeof(*info));
   info->gets = cache->reads;
   info->hits = cache->hits;
   info->sets = cache->writes;
@@ -221,6 +237,7 @@ svn_cache__get_info(svn_cache__t *cache,
 
 svn_string_t *
 svn_cache__format_info(const svn_cache__info_t *info,
+                       svn_boolean_t access_only,
                        apr_pool_t *result_pool)
 {
   enum { _1MB = 1024 * 1024 };
@@ -235,9 +252,40 @@ svn_cache__format_info(const svn_cache__
   double data_entry_rate = (100.0 * (double)info->used_entries)
                  / (double)(info->total_entries ? info->total_entries : 1);
 
-  return svn_string_createf(result_pool,
+  const char *histogram = "";
+  if (!access_only)
+    {
+      svn_stringbuf_t *text = svn_stringbuf_create_empty(result_pool);
+
+      int i;
+      int count = sizeof(info->histogram) / sizeof(info->histogram[0]);
+      for (i = 0; i < count; ++i)
+        if (info->histogram[i] > 0 || text->len > 0)
+          text = svn_stringbuf_createf(result_pool,
+                                       i == count - 1
+                                         ? "%12" APR_UINT64_T_FMT
+                                           " buckets with >%d entries\n%s"
+                                         : "%12" APR_UINT64_T_FMT
+                                           " buckets with %d entries\n%s",
+                                        info->histogram[i], i, text->data);
+
+      histogram = text->data;
+    }
+
+  return access_only
+       ? svn_string_createf(result_pool,
+                            "%s\n"
+                            "gets    : %" APR_UINT64_T_FMT
+                            ", %" APR_UINT64_T_FMT " hits (%5.2f%%)\n"
+                            "sets    : %" APR_UINT64_T_FMT
+                            " (%5.2f%% of misses)\n",
+                            info->id,
+                            info->gets,
+                            info->hits, hit_rate,
+                            info->sets, write_rate)
+       : svn_string_createf(result_pool,
 
-                            "prefix  : %s\n"
+                            "%s\n"
                             "gets    : %" APR_UINT64_T_FMT
                             ", %" APR_UINT64_T_FMT " hits (%5.2f%%)\n"
                             "sets    : %" APR_UINT64_T_FMT
@@ -247,7 +295,7 @@ svn_cache__format_info(const svn_cache__
                             " of %" APR_UINT64_T_FMT " MB data cache"
                             " / %" APR_UINT64_T_FMT " MB total cache memory\n"
                             "          %" APR_UINT64_T_FMT " entries (%5.2f%%)"
-                            " of %" APR_UINT64_T_FMT " total\n",
+                            " of %" APR_UINT64_T_FMT " total\n%s",
 
                             info->id,
 
@@ -261,5 +309,6 @@ svn_cache__format_info(const svn_cache__
                             info->total_size / _1MB,
 
                             info->used_entries, data_entry_rate,
-                            info->total_entries);
+                            info->total_entries,
+                            histogram);
 }

Modified: subversion/branches/cache-server/subversion/libsvn_subr/cache.h
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_subr/cache.h?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_subr/cache.h (original)
+++ subversion/branches/cache-server/subversion/libsvn_subr/cache.h Tue Oct 15 08:52:06 2013
@@ -38,6 +38,12 @@ typedef struct svn_cache__vtable_t {
                       const void *key,
                       apr_pool_t *result_pool);
 
+  /* See svn_cache__has_key(). */
+  svn_error_t *(*has_key)(svn_boolean_t *found,
+                          void *cache_implementation,
+                          const void *key,
+                          apr_pool_t *scratch_pool);
+
   /* See svn_cache__set(). */
   svn_error_t *(*set)(void *cache_implementation,
                       const void *key,

Modified: subversion/branches/cache-server/subversion/libsvn_subr/cache_config.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_subr/cache_config.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_subr/cache_config.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_subr/cache_config.c Tue Oct 15 08:52:06 2013
@@ -23,6 +23,7 @@
 #include <apr_atomic.h>
 
 #include "svn_cache_config.h"
+#include "private/svn_atomic.h"
 #include "private/svn_cache.h"
 
 #include "svn_pools.h"
@@ -69,30 +70,27 @@ svn_cache_config_get(void)
   return &cache_settings;
 }
 
-/* Access the process-global (singleton) membuffer cache. The first call
- * will automatically allocate the cache using the current cache config.
- * NULL will be returned if the desired cache size is 0 or if the cache
- * could not be created for some reason.
+/* Initializer function as required by svn_atomic__init_once.  Allocate
+ * the process-global (singleton) membuffer cache and return it in the
+ * svn_membuffer_t * in *BATON.  UNUSED_POOL is unused and should be NULL.
  */
-svn_membuffer_t *
-svn_cache__get_global_membuffer_cache(void)
+static svn_error_t *
+initialize_cache(void *baton, apr_pool_t *unused_pool)
 {
-  static svn_membuffer_t * volatile cache = NULL;
+  svn_membuffer_t **cache_p = baton;
+  svn_membuffer_t *cache = NULL;
 
   apr_uint64_t cache_size = cache_settings.cache_size;
-  if (!cache && cache_size)
+  if (cache_size)
     {
       svn_error_t *err;
 
-      svn_membuffer_t *old_cache = NULL;
-      svn_membuffer_t *new_cache = NULL;
-
       /* auto-allocate cache */
       apr_allocator_t *allocator = NULL;
       apr_pool_t *pool = NULL;
 
       if (apr_allocator_create(&allocator))
-        return NULL;
+        return SVN_NO_ERROR;
 
       /* Ensure that we free partially allocated data if we run OOM
        * before the cache is complete: If the cache cannot be allocated
@@ -112,13 +110,13 @@ svn_cache__get_global_membuffer_cache(vo
        */
       apr_pool_create_ex(&pool, NULL, NULL, allocator);
       if (pool == NULL)
-        return NULL;
+        return SVN_NO_ERROR;
       apr_allocator_owner_set(allocator, pool);
 
       err = svn_cache__membuffer_cache_create(
-          &new_cache,
+          &cache,
           (apr_size_t)cache_size,
-          (apr_size_t)(cache_size / 10),
+          (apr_size_t)(cache_size / 5),
           0,
           ! svn_cache_config_get()->single_threaded,
           FALSE,
@@ -129,33 +127,40 @@ svn_cache__get_global_membuffer_cache(vo
        */
       if (err)
         {
-          /* Memory and error cleanup */
-          svn_error_clear(err);
+          /* Memory cleanup */
           svn_pool_destroy(pool);
 
-          /* Prevent future attempts to create the cache. However, an
-           * existing cache instance (see next comment) remains valid.
-           */
+          /* Document that we actually don't have a cache. */
           cache_settings.cache_size = 0;
 
-          /* The current caller won't get the cache object.
-           * However, a concurrent call might have succeeded in creating
-           * the cache object. That call and all following ones will then
-           * use the successfully created cache instance.
-           */
-          return NULL;
+          return svn_error_trace(err);
         }
 
-      /* Handle race condition: if we are the first to create a
-       * cache object, make it our global singleton. Otherwise,
-       * discard the new cache and keep the existing one.
-       *
-       * Cast is necessary because of APR bug:
-       * https://issues.apache.org/bugzilla/show_bug.cgi?id=50731
-       */
-      old_cache = apr_atomic_casptr((volatile void **)&cache, new_cache, NULL);
-      if (old_cache != NULL)
-        svn_pool_destroy(pool);
+      /* done */
+      *cache_p = cache;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Access the process-global (singleton) membuffer cache. The first call
+ * will automatically allocate the cache using the current cache config.
+ * NULL will be returned if the desired cache size is 0 or if the cache
+ * could not be created for some reason.
+ */
+svn_membuffer_t *
+svn_cache__get_global_membuffer_cache(void)
+{
+  static svn_membuffer_t *cache = NULL;
+  static svn_atomic_t initialized = 0;
+
+  svn_error_t *err
+    = svn_atomic__init_once(&initialized, initialize_cache, &cache, NULL);
+  if (err)
+    {
+      /* no caches today ... */
+      svn_error_clear(err);
+      return NULL;
     }
 
   return cache;

Modified: subversion/branches/cache-server/subversion/libsvn_subr/cmdline.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_subr/cmdline.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_subr/cmdline.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_subr/cmdline.c Tue Oct 15 08:52:06 2013
@@ -42,6 +42,7 @@
 #include <apr_strings.h>        /* for apr_snprintf */
 #include <apr_pools.h>
 
+#include "svn_private_config.h"
 #include "svn_cmdline.h"
 #include "svn_ctype.h"
 #include "svn_dso.h"
@@ -64,15 +65,19 @@
 #include "private/svn_utf_private.h"
 #include "private/svn_string_private.h"
 
-#include "svn_private_config.h"
-
 #include "win32_crashrpt.h"
 
+#if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER < 1400)
+/* Before Visual Studio 2005, the C runtime didn't handle encodings for the
+   for the stdio output handling. */
+#define CMDLINE_USE_CUSTOM_ENCODING
+
 /* The stdin encoding. If null, it's the same as the native encoding. */
 static const char *input_encoding = NULL;
 
 /* The stdout encoding. If null, it's the same as the native encoding. */
 static const char *output_encoding = NULL;
+#endif
 
 
 int
@@ -114,7 +119,7 @@ svn_cmdline_init(const char *progname, F
 #endif
 
 #ifdef WIN32
-#if _MSC_VER < 1400
+#ifdef CMDLINE_USE_CUSTOM_ENCODING
   /* Initialize the input and output encodings. */
   {
     static char input_encoding_buffer[16];
@@ -128,33 +133,35 @@ svn_cmdline_init(const char *progname, F
                  "CP%u", (unsigned) GetConsoleOutputCP());
     output_encoding = output_encoding_buffer;
   }
-#endif /* _MSC_VER < 1400 */
+#endif /* CMDLINE_USE_CUSTOM_ENCODING */
 
 #ifdef SVN_USE_WIN32_CRASHHANDLER
-  /* Attach (but don't load) the crash handler */
-  SetUnhandledExceptionFilter(svn__unhandled_exception_filter);
+  if (!getenv("SVN_CMDLINE_DISABLE_CRASH_HANDLER"))
+    {
+      /* Attach (but don't load) the crash handler */
+      SetUnhandledExceptionFilter(svn__unhandled_exception_filter);
 
 #if _MSC_VER >= 1400
-  /* ### This should work for VC++ 2002 (=1300) and later */
-  /* Show the abort message on STDERR instead of a dialog to allow
-     scripts (e.g. our testsuite) to continue after an abort without
-     user intervention. Allow overriding for easier debugging. */
-  if (!getenv("SVN_CMDLINE_USE_DIALOG_FOR_ABORT"))
-    {
-      /* In release mode: Redirect abort() errors to stderr */
-      _set_error_mode(_OUT_TO_STDERR);
-
-      /* In _DEBUG mode: Redirect all debug output (E.g. assert() to stderr.
-         (Ignored in release builds) */
-      _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR);
-      _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR);
-      _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR);
-      _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
-      _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
-      _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
-    }
-#endif /* _MSC_VER >= 1400 */
+      /* ### This should work for VC++ 2002 (=1300) and later */
+      /* Show the abort message on STDERR instead of a dialog to allow
+         scripts (e.g. our testsuite) to continue after an abort without
+         user intervention. Allow overriding for easier debugging. */
+      if (!getenv("SVN_CMDLINE_USE_DIALOG_FOR_ABORT"))
+        {
+          /* In release mode: Redirect abort() errors to stderr */
+          _set_error_mode(_OUT_TO_STDERR);
 
+          /* In _DEBUG mode: Redirect all debug output (E.g. assert() to stderr.
+             (Ignored in release builds) */
+          _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR);
+          _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR);
+          _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR);
+          _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+          _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+          _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+        }
+#endif /* _MSC_VER >= 1400 */
+    }
 #endif /* SVN_USE_WIN32_CRASHHANDLER */
 
 #endif /* WIN32 */
@@ -236,7 +243,7 @@ svn_cmdline_init(const char *progname, F
   /* Create a pool for use by the UTF-8 routines.  It will be cleaned
      up by APR at exit time. */
   pool = svn_pool_create(NULL);
-  svn_utf_initialize2(pool, FALSE);
+  svn_utf_initialize2(FALSE, pool);
 
   if ((err = svn_nls_init()))
     {
@@ -256,10 +263,12 @@ svn_cmdline_cstring_from_utf8(const char
                               const char *src,
                               apr_pool_t *pool)
 {
-  if (output_encoding == NULL)
-    return svn_utf_cstring_from_utf8(dest, src, pool);
-  else
+#ifdef CMDLINE_USE_CUSTOM_ENCODING
+  if (output_encoding != NULL)
     return svn_utf_cstring_from_utf8_ex2(dest, src, output_encoding, pool);
+#endif
+
+  return svn_utf_cstring_from_utf8(dest, src, pool);
 }
 
 
@@ -277,10 +286,12 @@ svn_cmdline_cstring_to_utf8(const char *
                             const char *src,
                             apr_pool_t *pool)
 {
-  if (input_encoding == NULL)
-    return svn_utf_cstring_to_utf8(dest, src, pool);
-  else
+#ifdef CMDLINE_USE_CUSTOM_ENCODING
+  if (input_encoding != NULL)
     return svn_utf_cstring_to_utf8_ex2(dest, src, input_encoding, pool);
+#endif
+
+  return svn_utf_cstring_to_utf8(dest, src, pool);
 }
 
 
@@ -356,7 +367,7 @@ svn_cmdline_fputs(const char *string, FI
         {
           /* ### Issue #3014: Return a specific error for broken pipes,
            * ### with a single element in the error chain. */
-          if (APR_STATUS_IS_EPIPE(apr_get_os_error()))
+          if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error()))
             return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
           else
             return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
@@ -379,7 +390,7 @@ svn_cmdline_fflush(FILE *stream)
         {
           /* ### Issue #3014: Return a specific error for broken pipes,
            * ### with a single element in the error chain. */
-          if (APR_STATUS_IS_EPIPE(apr_get_os_error()))
+          if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error()))
             return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
           else
             return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
@@ -393,10 +404,12 @@ svn_cmdline_fflush(FILE *stream)
 
 const char *svn_cmdline_output_encoding(apr_pool_t *pool)
 {
+#ifdef CMDLINE_USE_CUSTOM_ENCODING
   if (output_encoding)
     return apr_pstrdup(pool, output_encoding);
-  else
-    return SVN_APR_LOCALE_CHARSET;
+#endif
+
+  return SVN_APR_LOCALE_CHARSET;
 }
 
 int

Modified: subversion/branches/cache-server/subversion/libsvn_subr/compat.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_subr/compat.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_subr/compat.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_subr/compat.c Tue Oct 15 08:52:06 2013
@@ -24,6 +24,7 @@
 #include <apr_pools.h>
 #include <apr_strings.h>
 
+#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_types.h"
 #include "svn_error.h"
@@ -95,23 +96,33 @@ svn_compat_log_revprops_in(apr_pool_t *p
 }
 
 void
-svn_compat_log_revprops_out(const char **author, const char **date,
-                            const char **message, apr_hash_t *revprops)
+svn_compat_log_revprops_out_string(const svn_string_t **author,
+                                   const svn_string_t **date,
+                                   const svn_string_t **message,
+                                   apr_hash_t *revprops)
 {
-  svn_string_t *author_s, *date_s,  *message_s;
-
   *author = *date = *message = NULL;
   if (revprops)
     {
-      if ((author_s = svn_hash_gets(revprops, SVN_PROP_REVISION_AUTHOR)))
-        *author = author_s->data;
-      if ((date_s = svn_hash_gets(revprops, SVN_PROP_REVISION_DATE)))
-        *date = date_s->data;
-      if ((message_s = svn_hash_gets(revprops, SVN_PROP_REVISION_LOG)))
-        *message = message_s->data;
+      *author = svn_hash_gets(revprops, SVN_PROP_REVISION_AUTHOR);
+      *date = svn_hash_gets(revprops, SVN_PROP_REVISION_DATE);
+      *message = svn_hash_gets(revprops, SVN_PROP_REVISION_LOG);
     }
 }
 
+void
+svn_compat_log_revprops_out(const char **author, const char **date,
+                            const char **message, apr_hash_t *revprops)
+{
+  const svn_string_t *author_s, *date_s,  *message_s;
+  svn_compat_log_revprops_out_string(&author_s, &date_s,  &message_s,
+                                     revprops);
+
+  *author = author_s ? author_s->data : NULL;
+  *date = date_s ? date_s->data : NULL;
+  *message = message_s ? message_s->data : NULL;
+}
+
 /* Baton for use with svn_compat_wrap_log_receiver */
 struct log_wrapper_baton {
   void *baton;

Modified: subversion/branches/cache-server/subversion/libsvn_subr/config.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_subr/config.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_subr/config.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_subr/config.c Tue Oct 15 08:52:06 2013
@@ -29,12 +29,13 @@
 
 #include <apr_general.h>
 #include <apr_lib.h>
+
+#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_error.h"
 #include "svn_pools.h"
 #include "config_impl.h"
 
-#include "svn_private_config.h"
 #include "private/svn_dep_compat.h"
 
 
@@ -188,7 +189,8 @@ read_all(svn_config_t **cfgp,
 #ifdef WIN32
   if (sys_registry_path)
     {
-      SVN_ERR(svn_config_read2(cfgp, sys_registry_path, FALSE, FALSE, pool));
+      SVN_ERR(svn_config_read3(cfgp, sys_registry_path, FALSE, FALSE, FALSE,
+                               pool));
       red_config = TRUE;
     }
 #endif /* WIN32 */
@@ -214,8 +216,8 @@ read_all(svn_config_t **cfgp,
         SVN_ERR(svn_config_merge(*cfgp, usr_registry_path, FALSE));
       else
         {
-          SVN_ERR(svn_config_read2(cfgp, usr_registry_path,
-                                   FALSE, FALSE, pool));
+          SVN_ERR(svn_config_read3(cfgp, usr_registry_path,
+                                   FALSE, FALSE, FALSE, pool));
           red_config = TRUE;
         }
     }

Modified: subversion/branches/cache-server/subversion/libsvn_subr/config_auth.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_subr/config_auth.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_subr/config_auth.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_subr/config_auth.c Tue Oct 15 08:52:06 2013
@@ -23,6 +23,7 @@
 
 
 
+#include "svn_private_config.h"
 #include "svn_dirent_uri.h"
 #include "svn_hash.h"
 #include "svn_io.h"
@@ -31,14 +32,8 @@
 
 #include "auth.h"
 
-#include "svn_private_config.h"
-
 #include "private/svn_auth_private.h"
 
-/* Helper for svn_config_{read|write}_auth_data.  Return a path to a
-   file within ~/.subversion/auth/ that holds CRED_KIND credentials
-   within REALMSTRING.  If no path is available *PATH will be set to
-   NULL. */
 svn_error_t *
 svn_auth__file_path(const char **path,
                     const char *cred_kind,
@@ -173,12 +168,6 @@ svn_config_walk_auth_data(const char *co
       NULL
     };
 
-  if (! config_dir)
-    {
-      /* Can't locate the cache to clear */
-      return SVN_NO_ERROR;
-    }
-
   iterpool = svn_pool_create(scratch_pool);
   for (i = 0; cred_kinds[i]; i++)
     {

Modified: subversion/branches/cache-server/subversion/libsvn_subr/config_file.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_subr/config_file.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_subr/config_file.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_subr/config_file.c Tue Oct 15 08:52:06 2013
@@ -94,7 +94,7 @@ parser_getc(parse_context_t *ctx, int *c
         }
       else if (ctx->buffer_pos < ctx->buffer_size)
         {
-          *c = ctx->parser_buffer[ctx->buffer_pos];
+          *c = (unsigned char)ctx->parser_buffer[ctx->buffer_pos];
           ctx->buffer_pos++;
         }
       else
@@ -107,7 +107,7 @@ parser_getc(parse_context_t *ctx, int *c
 
           if (ctx->buffer_pos < ctx->buffer_size)
             {
-              *c = ctx->parser_buffer[ctx->buffer_pos];
+              *c = (unsigned char)ctx->parser_buffer[ctx->buffer_pos];
               ctx->buffer_pos++;
             }
           else
@@ -131,7 +131,7 @@ parser_getc_plain(parse_context_t *ctx, 
 {
   if (ctx->buffer_pos < ctx->buffer_size)
     {
-      *c = ctx->parser_buffer[ctx->buffer_pos];
+      *c = (unsigned char)ctx->parser_buffer[ctx->buffer_pos];
       ctx->buffer_pos++;
 
       return SVN_NO_ERROR;
@@ -189,6 +189,32 @@ skip_to_eoln(parse_context_t *ctx, int *
   return SVN_NO_ERROR;
 }
 
+/* Skip a UTF-8 Byte Order Mark if found. */
+static APR_INLINE svn_error_t *
+skip_bom(parse_context_t *ctx)
+{
+  int ch;
+
+  SVN_ERR(parser_getc(ctx, &ch));
+  if (ch == 0xEF)
+    {
+      const unsigned char *buf = (unsigned char *)ctx->parser_buffer;
+      /* This makes assumptions about the implementation of parser_getc and
+       * the use of skip_bom.  Specifically that parser_getc() will get all
+       * of the BOM characters into the parse_context_t buffer.  This can
+       * safely be assumed as long as we only try to use skip_bom() at the
+       * start of the stream and the buffer is longer than 3 characters. */
+      SVN_ERR_ASSERT(ctx->buffer_size > ctx->buffer_pos + 1);
+      if (buf[ctx->buffer_pos] == 0xBB && buf[ctx->buffer_pos + 1] == 0xBF)
+        ctx->buffer_pos += 2;
+      else
+        SVN_ERR(parser_ungetc(ctx, ch));
+    }
+  else
+    SVN_ERR(parser_ungetc(ctx, ch));
+
+  return SVN_NO_ERROR;
+}
 
 /* Parse a single option value */
 static svn_error_t *
@@ -401,10 +427,13 @@ svn_config__parse_file(svn_config_t *cfg
                        svn_boolean_t must_exist, apr_pool_t *result_pool)
 {
   svn_error_t *err = SVN_NO_ERROR;
+  apr_file_t *apr_file;
   svn_stream_t *stream;
   apr_pool_t *scratch_pool = svn_pool_create(result_pool);
 
-  err = svn_stream_open_readonly(&stream, file, scratch_pool, scratch_pool);
+  /* Use unbuffered IO since we use our own buffering. */
+  err = svn_io_file_open(&apr_file, file, APR_READ, APR_OS_DEFAULT,
+                         scratch_pool);
 
   if (! must_exist && err && APR_STATUS_IS_ENOENT(err->apr_err))
     {
@@ -415,6 +444,7 @@ svn_config__parse_file(svn_config_t *cfg
   else
     SVN_ERR(err);
 
+  stream = svn_stream_from_aprfile2(apr_file, FALSE, scratch_pool);
   err = svn_config__parse_stream(cfg, stream, result_pool, scratch_pool);
 
   if (err != SVN_NO_ERROR)
@@ -450,6 +480,8 @@ svn_config__parse_stream(svn_config_t *c
   ctx->buffer_pos = 0;
   ctx->buffer_size = 0;
 
+  SVN_ERR(skip_bom(ctx));
+
   do
     {
       SVN_ERR(skip_whitespace(ctx, &ch, &count));
@@ -806,6 +838,8 @@ svn_config_ensure(const char *config_dir
         "###   http-max-connections       Maximum number of parallel server" NL
         "###                              connections to use for any given"  NL
         "###                              HTTP operation."                   NL
+        "###   http-chunked-requests      Whether to use chunked transfer"   NL
+        "###                              encoding for HTTP requests body."  NL
         "###   neon-debug-mask            Debug mask for Neon HTTP library"  NL
         "###   ssl-authority-files        List of files, each of a trusted CA"
                                                                              NL
@@ -1152,6 +1186,16 @@ svn_config_ensure(const char *config_dir
         "### ra_local (the file:// scheme). The value represents the number" NL
         "### of MB used by the cache."                                       NL
         "# memory-cache-size = 16"                                           NL
+        "### Set diff-ignore-content-type to 'yes' to cause 'svn diff' to"   NL
+        "### attempt to show differences of all modified files regardless"   NL
+        "### of their MIME content type.  By default, Subversion will only"  NL
+        "### attempt to show differences for files believed to have human-"  NL
+        "### readable (non-binary) content.  This option is especially"      NL
+        "### useful when Subversion is configured (via the 'diff-cmd'"       NL
+        "### option) to employ an external differencing tool which is able"  NL
+        "### to show meaningful differences for binary file formats.  [New"  NL
+        "### in 1.9]"                                                        NL
+        "# diff-ignore-content-type = no"                                    NL
         ""                                                                   NL
         "### Section for configuring automatic properties."                  NL
         "[auto-props]"                                                       NL

Modified: subversion/branches/cache-server/subversion/libsvn_subr/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_subr/deprecated.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_subr/deprecated.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_subr/deprecated.c Tue Oct 15 08:52:06 2013
@@ -32,6 +32,7 @@
    deprecated functions in this file. */
 #define SVN_DEPRECATED
 
+#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_subst.h"
 #include "svn_path.h"
@@ -48,9 +49,6 @@
 #include "private/svn_opt_private.h"
 #include "private/svn_mergeinfo_private.h"
 
-#include "svn_private_config.h"
-
-
 
 
 /*** Code. ***/
@@ -1252,13 +1250,27 @@ svn_xml_make_header(svn_stringbuf_t **st
   svn_xml_make_header2(str, NULL, pool);
 }
 
+
+/*** From utf.c ***/
 void
 svn_utf_initialize(apr_pool_t *pool)
 {
-  svn_utf_initialize2(pool, FALSE);
+  svn_utf_initialize2(FALSE, pool);
 }
 
 svn_error_t *
+svn_utf_cstring_from_utf8_ex(const char **dest,
+                             const char *src,
+                             const char *topage,
+                             const char *convset_key,
+                             apr_pool_t *pool)
+{
+  return svn_utf_cstring_from_utf8_ex2(dest, src, topage, pool);
+}
+
+
+/*** From subst.c ***/
+svn_error_t *
 svn_subst_build_keywords(svn_subst_keywords_t *kw,
                          const char *keywords_val,
                          const char *rev,
@@ -1301,4 +1313,11 @@ svn_subst_build_keywords(svn_subst_keywo
   return SVN_NO_ERROR;
 }
 
+/*** From version.c ***/
+svn_error_t *
+svn_ver_check_list(const svn_version_t *my_version,
+                   const svn_version_checklist_t *checklist)
+{
+  return svn_ver_check_list2(my_version, checklist, svn_ver_compatible);
+}
 

Modified: subversion/branches/cache-server/subversion/libsvn_subr/dirent_uri.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_subr/dirent_uri.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_subr/dirent_uri.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_subr/dirent_uri.c Tue Oct 15 08:52:06 2013
@@ -1688,7 +1688,9 @@ svn_dirent_is_canonical(const char *dire
 static svn_boolean_t
 relpath_is_canonical(const char *relpath)
 {
-  const char *ptr = relpath, *seg = relpath;
+  const char *dot_pos, *ptr = relpath;
+  apr_size_t i, len;
+  unsigned pattern = 0;
 
   /* RELPATH is canonical if it has:
    *  - no '.' segments
@@ -1696,35 +1698,38 @@ relpath_is_canonical(const char *relpath
    *  - no '//'
    */
 
-  if (*relpath == '\0')
-    return TRUE;
-
+  /* invalid beginnings */
   if (*ptr == '/')
     return FALSE;
 
-  /* Now validate the rest of the path. */
-  while(1)
-    {
-      apr_size_t seglen = ptr - seg;
-
-      if (seglen == 1 && *seg == '.')
-        return FALSE;  /*  /./   */
-
-      if (*ptr == '/' && *(ptr+1) == '/')
-        return FALSE;  /*  //    */
+  if (ptr[0] == '.' && (ptr[1] == '/' || ptr[1] == '\0'))
+    return FALSE;
 
-      if (! *ptr && *(ptr - 1) == '/')
-        return FALSE;  /* foo/  */
+  /* valid special cases */
+  len = strlen(ptr);
+  if (len < 2)
+    return TRUE;
 
-      if (! *ptr)
-        break;
+  /* invalid endings */
+  if (ptr[len-1] == '/' || (ptr[len-1] == '.' && ptr[len-2] == '/'))
+    return FALSE;
 
-      if (*ptr == '/')
-        ptr++;
-      seg = ptr;
+  /* '.' are rare. So, search for them globally. There will often be no 
+   * more than one hit.  Also note that we already checked for invalid 
+   * starts and endings, i.e. we only need to check for "/./"
+   */
+  for (dot_pos = memchr(ptr, '.', len);
+       dot_pos;
+       dot_pos = strchr(dot_pos+1, '.'))
+    if (dot_pos > ptr && dot_pos[-1] == '/' && dot_pos[1] == '/')
+      return FALSE;
 
-      while (*ptr && (*ptr != '/'))
-        ptr++;
+  /* Now validate the rest of the path. */
+  for (i = 0; i < len - 1; ++i)
+    {
+      pattern = ((pattern & 0xff) << 8) + (unsigned char)ptr[i];
+      if (pattern == 0x101 * (unsigned char)('/'))
+        return FALSE;
     }
 
   return TRUE;
@@ -1857,6 +1862,9 @@ svn_uri_is_canonical(const char *uri, ap
 #endif /* SVN_USE_DOS_PATHS */
 
   /* Now validate the rest of the URI. */
+  seg = ptr;
+  while (*ptr && (*ptr != '/'))
+    ptr++;
   while(1)
     {
       apr_size_t seglen = ptr - seg;
@@ -1875,9 +1883,8 @@ svn_uri_is_canonical(const char *uri, ap
 
       if (*ptr == '/')
         ptr++;
-      seg = ptr;
-
 
+      seg = ptr;
       while (*ptr && (*ptr != '/'))
         ptr++;
     }

Modified: subversion/branches/cache-server/subversion/libsvn_subr/dso.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_subr/dso.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_subr/dso.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_subr/dso.c Tue Oct 15 08:52:06 2013
@@ -22,10 +22,10 @@
 #include <apr_thread_mutex.h>
 #include <apr_hash.h>
 
+#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_dso.h"
 #include "svn_pools.h"
-#include "svn_private_config.h"
 
 #include "private/svn_mutex.h"
 

Modified: subversion/branches/cache-server/subversion/libsvn_subr/eol.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_subr/eol.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_subr/eol.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_subr/eol.c Tue Oct 15 08:52:06 2013
@@ -30,14 +30,12 @@
 #include "private/svn_eol_private.h"
 #include "private/svn_dep_compat.h"
 
-/* Machine-word-sized masks used in svn_eol__find_eol_start.
- */
 char *
 svn_eol__find_eol_start(char *buf, apr_size_t len)
 {
 #if !SVN_UNALIGNED_ACCESS_IS_OK
 
-  /* On some systems, we need to make sure that buf is properly aligned
+  /* On some systems, we need to make sure that BUF is properly aligned
    * for chunky data access. This overhead is still justified because
    * only lines tend to be tens of chars long.
    */
@@ -62,8 +60,8 @@ svn_eol__find_eol_start(char *buf, apr_s
     apr_uintptr_t r_test = chunk ^ SVN__R_MASK;
     apr_uintptr_t n_test = chunk ^ SVN__N_MASK;
 
-    /* A byte in SVN__R_TEST can by < 0x80, iff it has been \0 before
-     * (i.e. \r in *BUF). Dito for SVN__N_TEST. */
+    /* A byte in SVN__R_TEST can only be < 0x80, iff it has been \0 before
+     * (i.e. \r in *BUF). Ditto for SVN__N_TEST. */
     r_test |= (r_test & SVN__LOWER_7BITS_SET) + SVN__LOWER_7BITS_SET;
     n_test |= (n_test & SVN__LOWER_7BITS_SET) + SVN__LOWER_7BITS_SET;
 

Modified: subversion/branches/cache-server/subversion/libsvn_subr/error.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_subr/error.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_subr/error.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_subr/error.c Tue Oct 15 08:52:06 2013
@@ -110,7 +110,8 @@ make_error_internal(apr_status_t apr_err
     pool = child->pool;
   else
     {
-      if (apr_pool_create(&pool, NULL))
+      pool = svn_pool_create(NULL);
+      if (!pool)
         abort();
     }
 
@@ -339,7 +340,8 @@ svn_error_dup(svn_error_t *err)
   apr_pool_t *pool;
   svn_error_t *new_err = NULL, *tmp_err = NULL;
 
-  if (apr_pool_create(&pool, NULL))
+  pool = svn_pool_create(NULL);
+  if (!pool)
     abort();
 
   for (; err; err = err->child)
@@ -559,7 +561,7 @@ svn_handle_error2(svn_error_t *err,
      preferring apr_pool_*() instead.  I can't remember why -- it may
      be an artifact of r843793, or it may be for some deeper reason --
      but I'm playing it safe and using apr_pool_*() here too. */
-  apr_pool_create(&subpool, err->pool);
+  subpool = svn_pool_create(err->pool);
   empties = apr_array_make(subpool, 0, sizeof(apr_status_t));
 
   tmp_err = err;
@@ -679,7 +681,7 @@ svn_strerror(apr_status_t statcode, char
 }
 
 #ifdef SVN_DEBUG
-/* Defines svn__errno */
+/* Defines svn__errno and svn__apr_errno */
 #include "errorcode.inc"
 #endif
 
@@ -705,6 +707,12 @@ svn_error_symbolic_name(apr_status_t sta
   for (i = 0; i < sizeof(svn__errno) / sizeof(svn__errno[0]); i++)
     if (svn__errno[i].errcode == (int)statcode)
       return svn__errno[i].errname;
+
+  /* Try APR errors. */
+  /* Linear search through a sorted array */
+  for (i = 0; i < sizeof(svn__apr_errno) / sizeof(svn__apr_errno[0]); i++)
+    if (svn__apr_errno[i].errcode == (int)statcode)
+      return svn__apr_errno[i].errname;
 #endif /* SVN_DEBUG */
 
   /* ### TODO: do we need APR_* error macros?  What about APR_TO_OS_ERROR()? */
@@ -765,6 +773,12 @@ svn_error_set_malfunction_handler(svn_er
   return old_malfunction_handler;
 }
 
+svn_error_malfunction_handler_t
+svn_error_get_malfunction_handler(void)
+{
+  return malfunction_handler;
+}
+
 /* Note: Although this is a "__" function, it is in the public ABI, so
  * we can never remove it or change its signature. */
 svn_error_t *

Modified: subversion/branches/cache-server/subversion/libsvn_subr/gpg_agent.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_subr/gpg_agent.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_subr/gpg_agent.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_subr/gpg_agent.c Tue Oct 15 08:52:06 2013
@@ -156,42 +156,28 @@ send_option(int sd, char *buf, size_t n,
   return (strncmp(buf, "OK", 2) == 0);
 }
 
-/* Implementation of svn_auth__password_get_t that retrieves the password
-   from gpg-agent */
+
+/* Locate a running GPG Agent, and return an open file descriptor
+ * for communication with the agent in *NEW_SD. If no running agent
+ * can be found, set *NEW_SD to -1. */
 static svn_error_t *
-password_get_gpg_agent(svn_boolean_t *done,
-                       const char **password,
-                       apr_hash_t *creds,
-                       const char *realmstring,
-                       const char *username,
-                       apr_hash_t *parameters,
-                       svn_boolean_t non_interactive,
-                       apr_pool_t *pool)
+find_running_gpg_agent(int *new_sd, apr_pool_t *pool)
 {
-  int sd;
+  char *buffer;
   char *gpg_agent_info = NULL;
+  const char *socket_name = NULL;
+  const char *request = NULL;
   const char *p = NULL;
   char *ep = NULL;
-  char *buffer;
-
-  apr_array_header_t *socket_details;
-  const char *request = NULL;
-  const char *cache_id = NULL;
-  struct sockaddr_un addr;
-  const char *tty_name;
-  const char *tty_type;
-  const char *lc_ctype;
-  const char *display;
-  const char *socket_name = NULL;
-  svn_checksum_t *digest = NULL;
-  char *password_prompt;
-  char *realm_prompt;
+  int sd;
 
-  *done = FALSE;
+  *new_sd = -1;
 
   gpg_agent_info = getenv("GPG_AGENT_INFO");
   if (gpg_agent_info != NULL)
     {
+      apr_array_header_t *socket_details;
+
       socket_details = svn_cstring_split(gpg_agent_info, ":", TRUE,
                                          pool);
       socket_name = APR_ARRAY_IDX(socket_details, 0, const char *);
@@ -201,6 +187,8 @@ password_get_gpg_agent(svn_boolean_t *do
 
   if (socket_name != NULL)
     {
+      struct sockaddr_un addr;
+
       addr.sun_family = AF_UNIX;
       strncpy(addr.sun_path, socket_name, sizeof(addr.sun_path) - 1);
       addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
@@ -273,6 +261,44 @@ password_get_gpg_agent(svn_boolean_t *do
       return SVN_NO_ERROR;
     }
 
+  *new_sd = sd;
+  return SVN_NO_ERROR;
+}
+
+/* Implementation of svn_auth__password_get_t that retrieves the password
+   from gpg-agent */
+static svn_error_t *
+password_get_gpg_agent(svn_boolean_t *done,
+                       const char **password,
+                       apr_hash_t *creds,
+                       const char *realmstring,
+                       const char *username,
+                       apr_hash_t *parameters,
+                       svn_boolean_t non_interactive,
+                       apr_pool_t *pool)
+{
+  int sd;
+  const char *p = NULL;
+  char *ep = NULL;
+  char *buffer;
+  const char *request = NULL;
+  const char *cache_id = NULL;
+  const char *tty_name;
+  const char *tty_type;
+  const char *lc_ctype;
+  const char *display;
+  svn_checksum_t *digest = NULL;
+  char *password_prompt;
+  char *realm_prompt;
+
+  *done = FALSE;
+
+  SVN_ERR(find_running_gpg_agent(&sd, pool));
+  if (sd == -1)
+    return SVN_NO_ERROR;
+
+  buffer = apr_palloc(pool, BUFFER_SIZE);
+
   /* Send TTY_NAME to the gpg-agent daemon. */
   tty_name = getenv("GPG_TTY");
   if (tty_name != NULL)
@@ -283,11 +309,6 @@ password_get_gpg_agent(svn_boolean_t *do
           return SVN_NO_ERROR;
         }
     }
-  else
-    {
-      close(sd);
-      return SVN_NO_ERROR;
-    }
 
   /* Send TTY_TYPE to the gpg-agent daemon. */
   tty_type = getenv("TERM");
@@ -299,11 +320,6 @@ password_get_gpg_agent(svn_boolean_t *do
           return SVN_NO_ERROR;
         }
     }
-  else
-    {
-      close(sd);
-      return SVN_NO_ERROR;
-    }
 
   /* Compute LC_CTYPE. */
   lc_ctype = getenv("LC_ALL");
@@ -388,8 +404,8 @@ password_get_gpg_agent(svn_boolean_t *do
    password in GPG Agent if that's how this particular integration
    worked.  But it isn't.  GPG Agent stores the password provided by
    the user via the pinentry program immediately upon its provision
-   (and regardless of its accuracy as passwords go), so there's
-   nothing really to do here.  */
+   (and regardless of its accuracy as passwords go), so we just need
+   to check if a running GPG Agent exists. */
 static svn_error_t *
 password_set_gpg_agent(svn_boolean_t *done,
                        apr_hash_t *creds,
@@ -400,6 +416,15 @@ password_set_gpg_agent(svn_boolean_t *do
                        svn_boolean_t non_interactive,
                        apr_pool_t *pool)
 {
+  int sd;
+
+  *done = FALSE;
+
+  SVN_ERR(find_running_gpg_agent(&sd, pool));
+  if (sd == -1)
+    return SVN_NO_ERROR;
+
+  close(sd);
   *done = TRUE;
 
   return SVN_NO_ERROR;