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