1289177Speter/*
2289177Speter * config_pool.c :  pool of configuration objects
3289177Speter *
4289177Speter * ====================================================================
5289177Speter *    Licensed to the Apache Software Foundation (ASF) under one
6289177Speter *    or more contributor license agreements.  See the NOTICE file
7289177Speter *    distributed with this work for additional information
8289177Speter *    regarding copyright ownership.  The ASF licenses this file
9289177Speter *    to you under the Apache License, Version 2.0 (the
10289177Speter *    "License"); you may not use this file except in compliance
11289177Speter *    with the License.  You may obtain a copy of the License at
12289177Speter *
13289177Speter *      http://www.apache.org/licenses/LICENSE-2.0
14289177Speter *
15289177Speter *    Unless required by applicable law or agreed to in writing,
16289177Speter *    software distributed under the License is distributed on an
17289177Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18289177Speter *    KIND, either express or implied.  See the License for the
19289177Speter *    specific language governing permissions and limitations
20289177Speter *    under the License.
21289177Speter * ====================================================================
22289177Speter */
23289177Speter
24289177Speter
25289177Speter
26289177Speter
27289177Speter#include <assert.h>
28289177Speter
29289177Speter#include "svn_error.h"
30289177Speter#include "svn_hash.h"
31289177Speter#include "svn_pools.h"
32289177Speter
33289177Speter#include "private/svn_atomic.h"
34289177Speter#include "private/svn_object_pool.h"
35289177Speter#include "private/svn_subr_private.h"
36289177Speter#include "private/svn_dep_compat.h"
37289177Speter
38289177Speter
39289177Speter
40289177Speter/* A reference counting wrapper around the user-provided object.
41289177Speter */
42289177Spetertypedef struct object_ref_t
43289177Speter{
44289177Speter  /* reference to the parent container */
45289177Speter  svn_object_pool__t *object_pool;
46289177Speter
47289177Speter  /* identifies the bucket in OBJECT_POOL->OBJECTS in which this entry
48289177Speter   * belongs. */
49289177Speter  svn_membuf_t key;
50289177Speter
51289177Speter  /* User provided object. Usually a wrapper. */
52289177Speter  void *wrapper;
53289177Speter
54289177Speter  /* private pool. This instance and its other members got allocated in it.
55289177Speter   * Will be destroyed when this instance is cleaned up. */
56289177Speter  apr_pool_t *pool;
57289177Speter
58289177Speter  /* Number of references to this data struct */
59289177Speter  volatile svn_atomic_t ref_count;
60289177Speter} object_ref_t;
61289177Speter
62289177Speter
63289177Speter/* Core data structure.  All access to it must be serialized using MUTEX.
64289177Speter */
65289177Speterstruct svn_object_pool__t
66289177Speter{
67289177Speter  /* serialization object for all non-atomic data in this struct */
68289177Speter  svn_mutex__t *mutex;
69289177Speter
70289177Speter  /* object_ref_t.KEY -> object_ref_t* mapping.
71289177Speter   *
72289177Speter   * In shared object mode, there is at most one such entry per key and it
73289177Speter   * may or may not be in use.  In exclusive mode, only unused references
74289177Speter   * will be put here and they form chains if there are multiple unused
75289177Speter   * instances for the key. */
76289177Speter  apr_hash_t *objects;
77289177Speter
78289177Speter  /* same as objects->count but allows for non-sync'ed access */
79289177Speter  volatile svn_atomic_t object_count;
80289177Speter
81289177Speter  /* Number of entries in OBJECTS with a reference count 0.
82289177Speter     Due to races, this may be *temporarily* off by one or more.
83289177Speter     Hence we must not strictly depend on it. */
84289177Speter  volatile svn_atomic_t unused_count;
85289177Speter
86289177Speter  /* the root pool owning this structure */
87289177Speter  apr_pool_t *pool;
88289177Speter
89289177Speter  /* extractor and updater for the user object wrappers */
90289177Speter  svn_object_pool__getter_t getter;
91289177Speter  svn_object_pool__setter_t setter;
92289177Speter};
93289177Speter
94289177Speter
95289177Speter/* Pool cleanup function for the whole object pool.
96289177Speter */
97289177Speterstatic apr_status_t
98289177Speterobject_pool_cleanup(void *baton)
99289177Speter{
100289177Speter  svn_object_pool__t *object_pool = baton;
101289177Speter
102289177Speter  /* all entries must have been released up by now */
103289177Speter  SVN_ERR_ASSERT_NO_RETURN(   object_pool->object_count
104289177Speter                           == object_pool->unused_count);
105289177Speter
106289177Speter  return APR_SUCCESS;
107289177Speter}
108289177Speter
109289177Speter/* Remove entries from OBJECTS in OBJECT_POOL that have a ref-count of 0.
110289177Speter *
111289177Speter * Requires external serialization on OBJECT_POOL.
112289177Speter */
113289177Speterstatic void
114289177Speterremove_unused_objects(svn_object_pool__t *object_pool)
115289177Speter{
116289177Speter  apr_pool_t *subpool = svn_pool_create(object_pool->pool);
117289177Speter
118289177Speter  /* process all hash buckets */
119289177Speter  apr_hash_index_t *hi;
120289177Speter  for (hi = apr_hash_first(subpool, object_pool->objects);
121289177Speter       hi != NULL;
122289177Speter       hi = apr_hash_next(hi))
123289177Speter    {
124289177Speter      object_ref_t *object_ref = apr_hash_this_val(hi);
125289177Speter
126289177Speter      /* note that we won't hand out new references while access
127289177Speter         to the hash is serialized */
128289177Speter      if (svn_atomic_read(&object_ref->ref_count) == 0)
129289177Speter        {
130289177Speter          apr_hash_set(object_pool->objects, object_ref->key.data,
131289177Speter                       object_ref->key.size, NULL);
132289177Speter          svn_atomic_dec(&object_pool->object_count);
133289177Speter          svn_atomic_dec(&object_pool->unused_count);
134289177Speter
135289177Speter          svn_pool_destroy(object_ref->pool);
136289177Speter        }
137289177Speter    }
138289177Speter
139289177Speter  svn_pool_destroy(subpool);
140289177Speter}
141289177Speter
142289177Speter/* Cleanup function called when an object_ref_t gets released.
143289177Speter */
144289177Speterstatic apr_status_t
145289177Speterobject_ref_cleanup(void *baton)
146289177Speter{
147289177Speter  object_ref_t *object = baton;
148289177Speter  svn_object_pool__t *object_pool = object->object_pool;
149289177Speter
150289177Speter  /* If we released the last reference to object, there is one more
151289177Speter     unused entry.
152289177Speter
153289177Speter     Note that unused_count does not need to be always exact but only
154289177Speter     needs to become exact *eventually* (we use it to check whether we
155289177Speter     should remove unused objects every now and then).  I.e. it must
156289177Speter     never drift off / get stuck but always reflect the true value once
157289177Speter     all threads left the racy sections.
158289177Speter   */
159289177Speter  if (svn_atomic_dec(&object->ref_count) == 0)
160289177Speter    svn_atomic_inc(&object_pool->unused_count);
161289177Speter
162289177Speter  return APR_SUCCESS;
163289177Speter}
164289177Speter
165289177Speter/* Handle reference counting for the OBJECT_REF that the caller is about
166289177Speter * to return.  The reference will be released when POOL gets cleaned up.
167289177Speter *
168289177Speter * Requires external serialization on OBJECT_REF->OBJECT_POOL.
169289177Speter */
170289177Speterstatic void
171289177Speteradd_object_ref(object_ref_t *object_ref,
172289177Speter              apr_pool_t *pool)
173289177Speter{
174289177Speter  /* Update ref counter.
175289177Speter     Note that this is racy with object_ref_cleanup; see comment there. */
176289177Speter  if (svn_atomic_inc(&object_ref->ref_count) == 0)
177289177Speter    svn_atomic_dec(&object_ref->object_pool->unused_count);
178289177Speter
179289177Speter  /* make sure the reference gets released automatically */
180289177Speter  apr_pool_cleanup_register(pool, object_ref, object_ref_cleanup,
181289177Speter                            apr_pool_cleanup_null);
182289177Speter}
183289177Speter
184289177Speter/* Actual implementation of svn_object_pool__lookup.
185289177Speter *
186289177Speter * Requires external serialization on OBJECT_POOL.
187289177Speter */
188289177Speterstatic svn_error_t *
189289177Speterlookup(void **object,
190289177Speter       svn_object_pool__t *object_pool,
191289177Speter       svn_membuf_t *key,
192289177Speter       void *baton,
193289177Speter       apr_pool_t *result_pool)
194289177Speter{
195289177Speter  object_ref_t *object_ref
196289177Speter    = apr_hash_get(object_pool->objects, key->data, key->size);
197289177Speter
198289177Speter  if (object_ref)
199289177Speter    {
200289177Speter      *object = object_pool->getter(object_ref->wrapper, baton, result_pool);
201289177Speter      add_object_ref(object_ref, result_pool);
202289177Speter    }
203289177Speter  else
204289177Speter    {
205289177Speter      *object = NULL;
206289177Speter    }
207289177Speter
208289177Speter  return SVN_NO_ERROR;
209289177Speter}
210289177Speter
211289177Speter/* Actual implementation of svn_object_pool__insert.
212289177Speter *
213289177Speter * Requires external serialization on OBJECT_POOL.
214289177Speter */
215289177Speterstatic svn_error_t *
216289177Speterinsert(void **object,
217289177Speter       svn_object_pool__t *object_pool,
218289177Speter       const svn_membuf_t *key,
219289177Speter       void *wrapper,
220289177Speter       void *baton,
221289177Speter       apr_pool_t *wrapper_pool,
222289177Speter       apr_pool_t *result_pool)
223289177Speter{
224289177Speter  object_ref_t *object_ref
225289177Speter    = apr_hash_get(object_pool->objects, key->data, key->size);
226289177Speter  if (object_ref)
227289177Speter    {
228289177Speter      /* entry already exists (e.g. race condition) */
229289177Speter      svn_error_t *err = object_pool->setter(&object_ref->wrapper,
230289177Speter                                             wrapper, baton,
231289177Speter                                             object_ref->pool);
232289177Speter      if (err)
233289177Speter        {
234289177Speter          /* if we had an issue in the setter, then OBJECT_REF is in an
235289177Speter           * unknown state now.  Keep it around for the current users
236289177Speter           * (i.e. don't clean the pool) but remove it from the list of
237289177Speter           * available ones.
238289177Speter           */
239289177Speter          apr_hash_set(object_pool->objects, key->data, key->size, NULL);
240289177Speter          svn_atomic_dec(&object_pool->object_count);
241289177Speter
242289177Speter          /* for the unlikely case that the object got created _and_
243289177Speter           * already released since we last checked: */
244289177Speter          if (svn_atomic_read(&object_ref->ref_count) == 0)
245289177Speter            svn_atomic_dec(&object_pool->unused_count);
246289177Speter
247289177Speter          /* cleanup the new data as well because it's not safe to use
248289177Speter           * either.
249289177Speter           */
250289177Speter          svn_pool_destroy(wrapper_pool);
251289177Speter
252289177Speter          /* propagate error */
253289177Speter          return svn_error_trace(err);
254289177Speter        }
255289177Speter
256289177Speter      /* Destroy the new one and return a reference to the existing one
257289177Speter       * because the existing one may already have references on it.
258289177Speter       */
259289177Speter      svn_pool_destroy(wrapper_pool);
260289177Speter    }
261289177Speter  else
262289177Speter    {
263289177Speter      /* add new index entry */
264289177Speter      object_ref = apr_pcalloc(wrapper_pool, sizeof(*object_ref));
265289177Speter      object_ref->object_pool = object_pool;
266289177Speter      object_ref->wrapper = wrapper;
267289177Speter      object_ref->pool = wrapper_pool;
268289177Speter
269289177Speter      svn_membuf__create(&object_ref->key, key->size, wrapper_pool);
270289177Speter      object_ref->key.size = key->size;
271289177Speter      memcpy(object_ref->key.data, key->data, key->size);
272289177Speter
273289177Speter      apr_hash_set(object_pool->objects, object_ref->key.data,
274289177Speter                   object_ref->key.size, object_ref);
275289177Speter      svn_atomic_inc(&object_pool->object_count);
276289177Speter
277289177Speter      /* the new entry is *not* in use yet.
278289177Speter       * add_object_ref will update counters again.
279289177Speter       */
280289177Speter      svn_atomic_inc(&object_ref->object_pool->unused_count);
281289177Speter    }
282289177Speter
283289177Speter  /* return a reference to the object we just added */
284289177Speter  *object = object_pool->getter(object_ref->wrapper, baton, result_pool);
285289177Speter  add_object_ref(object_ref, result_pool);
286289177Speter
287289177Speter  /* limit memory usage */
288289177Speter  if (svn_atomic_read(&object_pool->unused_count) * 2
289289177Speter      > apr_hash_count(object_pool->objects) + 2)
290289177Speter    remove_unused_objects(object_pool);
291289177Speter
292289177Speter  return SVN_NO_ERROR;
293289177Speter}
294289177Speter
295289177Speter/* Implement svn_object_pool__getter_t as no-op.
296289177Speter */
297289177Speterstatic void *
298289177Speterdefault_getter(void *object,
299289177Speter               void *baton,
300289177Speter               apr_pool_t *pool)
301289177Speter{
302289177Speter  return object;
303289177Speter}
304289177Speter
305289177Speter/* Implement svn_object_pool__setter_t as no-op.
306289177Speter */
307289177Speterstatic svn_error_t *
308289177Speterdefault_setter(void **target,
309289177Speter               void *source,
310289177Speter               void *baton,
311289177Speter               apr_pool_t *pool)
312289177Speter{
313289177Speter  return SVN_NO_ERROR;
314289177Speter}
315289177Speter
316289177Speter
317289177Speter/* API implementation */
318289177Speter
319289177Spetersvn_error_t *
320289177Spetersvn_object_pool__create(svn_object_pool__t **object_pool,
321289177Speter                        svn_object_pool__getter_t getter,
322289177Speter                        svn_object_pool__setter_t setter,
323289177Speter                        svn_boolean_t thread_safe,
324289177Speter                        apr_pool_t *pool)
325289177Speter{
326289177Speter  svn_object_pool__t *result;
327289177Speter
328289177Speter  /* construct the object pool in our private ROOT_POOL to survive POOL
329289177Speter   * cleanup and to prevent threading issues with the allocator
330289177Speter   */
331289177Speter  result = apr_pcalloc(pool, sizeof(*result));
332289177Speter  SVN_ERR(svn_mutex__init(&result->mutex, thread_safe, pool));
333289177Speter
334289177Speter  result->pool = pool;
335289177Speter  result->objects = svn_hash__make(result->pool);
336289177Speter  result->getter = getter ? getter : default_getter;
337289177Speter  result->setter = setter ? setter : default_setter;
338289177Speter
339289177Speter  /* make sure we clean up nicely.
340289177Speter   * We need two cleanup functions of which exactly one will be run
341289177Speter   * (disabling the respective other as the first step).  If the owning
342289177Speter   * pool does not cleaned up / destroyed explicitly, it may live longer
343289177Speter   * than our allocator.  So, we need do act upon cleanup requests from
344289177Speter   * either side - owning_pool and root_pool.
345289177Speter   */
346289177Speter  apr_pool_cleanup_register(pool, result, object_pool_cleanup,
347289177Speter                            apr_pool_cleanup_null);
348289177Speter
349289177Speter  *object_pool = result;
350289177Speter  return SVN_NO_ERROR;
351289177Speter}
352289177Speter
353289177Speterapr_pool_t *
354289177Spetersvn_object_pool__new_wrapper_pool(svn_object_pool__t *object_pool)
355289177Speter{
356289177Speter  return svn_pool_create(object_pool->pool);
357289177Speter}
358289177Speter
359289177Spetersvn_mutex__t *
360289177Spetersvn_object_pool__mutex(svn_object_pool__t *object_pool)
361289177Speter{
362289177Speter  return object_pool->mutex;
363289177Speter}
364289177Speter
365289177Speterunsigned
366289177Spetersvn_object_pool__count(svn_object_pool__t *object_pool)
367289177Speter{
368289177Speter  return svn_atomic_read(&object_pool->object_count);
369289177Speter}
370289177Speter
371289177Spetersvn_error_t *
372289177Spetersvn_object_pool__lookup(void **object,
373289177Speter                        svn_object_pool__t *object_pool,
374289177Speter                        svn_membuf_t *key,
375289177Speter                        void *baton,
376289177Speter                        apr_pool_t *result_pool)
377289177Speter{
378289177Speter  *object = NULL;
379289177Speter  SVN_MUTEX__WITH_LOCK(object_pool->mutex,
380289177Speter                       lookup(object, object_pool, key, baton, result_pool));
381289177Speter  return SVN_NO_ERROR;
382289177Speter}
383289177Speter
384289177Spetersvn_error_t *
385289177Spetersvn_object_pool__insert(void **object,
386289177Speter                        svn_object_pool__t *object_pool,
387289177Speter                        const svn_membuf_t *key,
388289177Speter                        void *wrapper,
389289177Speter                        void *baton,
390289177Speter                        apr_pool_t *wrapper_pool,
391289177Speter                        apr_pool_t *result_pool)
392289177Speter{
393289177Speter  *object = NULL;
394289177Speter  SVN_MUTEX__WITH_LOCK(object_pool->mutex,
395289177Speter                       insert(object, object_pool, key, wrapper, baton,
396289177Speter                              wrapper_pool, result_pool));
397289177Speter  return SVN_NO_ERROR;
398289177Speter}
399