Alien-SVN

 view release on metacpan or  search on metacpan

src/subversion/subversion/libsvn_fs_fs/tree.c  view on Meta::CPAN

  /* the node allocated in the cache's pool. NULL for empty entries. */
  dag_node_t *node;
} cache_entry_t;

/* Number of entries in the cache.  Keep this low to keep pressure on the
   CPU caches low as well.  A binary value is most efficient.  If we walk
   a directory tree, we want enough entries to store nodes for all files
   without overwriting the nodes for the parent folder.  That way, there
   will be no unnecessary misses (except for a few random ones caused by
   hash collision).

   The actual number of instances may be higher but entries that got
   overwritten are no longer visible.
 */
enum { BUCKET_COUNT = 256 };

/* Each pool that has received a DAG node, will hold at least on lock on
   our cache to ensure that the node remains valid despite being allocated
   in the cache's pool.  This is the structure to represent the lock.
 */
typedef struct cache_lock_t
{
  /* pool holding the lock */
  apr_pool_t *pool;

  /* cache being locked */
  fs_fs_dag_cache_t *cache;

  /* next lock. NULL at EOL */
  struct cache_lock_t *next;

  /* previous lock. NULL at list head. Only then this==cache->first_lock */
  struct cache_lock_t *prev;
} cache_lock_t;

/* The actual cache structure.  All nodes will be allocated in POOL.
   When the number of INSERTIONS (i.e. objects created form that pool)
   exceeds a certain threshold, the pool will be cleared and the cache
   with it.

   To ensure that nodes returned from this structure remain valid, the
   cache will get locked for the lifetime of the _receiving_ pools (i.e.
   those in which we would allocate the node if there was no cache.).
   The cache will only be cleared FIRST_LOCK is 0.
 */
struct fs_fs_dag_cache_t
{
  /* fixed number of (possibly empty) cache entries */
  cache_entry_t buckets[BUCKET_COUNT];

  /* pool used for all node allocation */
  apr_pool_t *pool;

  /* number of entries created from POOL since the last cleanup */
  apr_size_t insertions;

  /* Property lookups etc. have a very high locality (75% re-hit).
     Thus, remember the last hit location for optimistic lookup. */
  apr_size_t last_hit;

  /* List of receiving pools that are still alive. */
  cache_lock_t *first_lock;
};

/* Cleanup function to be called when a receiving pool gets cleared.
   Unlocks the cache once.
 */
static apr_status_t
unlock_cache(void *baton_void)
{
  cache_lock_t *lock = baton_void;

  /* remove lock from chain. Update the head */
  if (lock->next)
    lock->next->prev = lock->prev;
  if (lock->prev)
    lock->prev->next = lock->next;
  else
    lock->cache->first_lock = lock->next;

  return APR_SUCCESS;
}

/* Cleanup function to be called when the cache itself gets destroyed.
   In that case, we must unregister all unlock requests.
 */
static apr_status_t
unregister_locks(void *baton_void)
{
  fs_fs_dag_cache_t *cache = baton_void;
  cache_lock_t *lock;

  for (lock = cache->first_lock; lock; lock = lock->next)
    apr_pool_cleanup_kill(lock->pool,
                          lock,
                          unlock_cache);

  return APR_SUCCESS;
}

fs_fs_dag_cache_t*
svn_fs_fs__create_dag_cache(apr_pool_t *pool)
{
  fs_fs_dag_cache_t *result = apr_pcalloc(pool, sizeof(*result));
  result->pool = svn_pool_create(pool);

  apr_pool_cleanup_register(pool,
                            result,
                            unregister_locks,
                            apr_pool_cleanup_null);

  return result;
}

/* Prevent the entries in CACHE from being destroyed, for as long as the
   POOL lives.
 */
static void
lock_cache(fs_fs_dag_cache_t* cache, apr_pool_t *pool)
{
  /* we only need to lock / unlock once per pool.  Since we will often ask



( run in 1.287 second using v1.01-cache-2.11-cpan-df04353d9ac )