Index: subversion/libsvn_fs_base/bdb/env.c =================================================================== --- subversion/libsvn_fs_base/bdb/env.c (revision 20924) +++ subversion/libsvn_fs_base/bdb/env.c (working copy) @@ -24,13 +24,13 @@ #include #endif -#include #include #include #include "svn_path.h" #include "svn_pools.h" #include "svn_utf.h" +#include "svn_atomic.h" #include "bdb-err.h" #include "bdb_compat.h" @@ -61,30 +61,7 @@ those systems anyway, so we'll rely on APR_HAS_THREADS.) */ - -/* The apr_atomic API changed somewhat between apr-0.x and apr-1.x. - ### Should we move these defines to svn_private_config.h? */ -/* ### Note: svn__atomic_cas should not be combined with the other - svn__atomic operations. A comment in apr_atomic.h explains - that on some platforms, the CAS function is implemented in a - way that is incompatible with the other atomic operations. */ -#include -#if APR_MAJOR_VERSION > 0 -# define svn__atomic_t apr_uint32_t -# define svn__atomic_read(mem) apr_atomic_read32((mem)) -# define svn__atomic_set(mem, val) apr_atomic_set32((mem), (val)) -# define svn__atomic_cas(mem, with, cmp) \ - apr_atomic_cas32((mem), (with), (cmp)) -#else -# define svn__atomic_t apr_atomic_t -# define svn__atomic_read(mem) apr_atomic_read((mem)) -# define svn__atomic_set(mem, val) apr_atomic_set((mem), (val)) -# define svn__atomic_cas(mem, with, cmp) \ - apr_atomic_cas((mem), (with), (cmp)) -#endif /* APR_MAJOR_VERSION */ - - /* The cache key for a Berkeley DB environment descriptor. This is a combination of the device ID and INODE number of the Berkeley DB config file. @@ -153,8 +130,8 @@ Note 2: Unlike other fields in this structure, this field is not protected by the cache mutex on threaded platforms, and - should only be accesses via the svn__atomic functions. */ - volatile svn__atomic_t panic; + should only be accesses via the svn_atomic functions. */ + volatile svn_atomic_t panic; /* The key for the environment descriptor cache. */ bdb_env_key_t key; @@ -373,81 +350,38 @@ bdb_cache_lock = NULL; return APR_SUCCESS; } - -/* Magic values for atomic initialization of the environment cache. */ -#define BDB_CACHE_UNINITIALIZED 0 -#define BDB_CACHE_START_INIT 1 -#define BDB_CACHE_INIT_FAILED 2 -#define BDB_CACHE_INITIALIZED 3 -static volatile svn__atomic_t bdb_cache_state = BDB_CACHE_UNINITIALIZED; #endif /* APR_HAS_THREADS */ +static volatile svn_atomic_t bdb_cache_state = SVN_UNINITIALIZED; svn_error_t * -svn_fs_bdb__init(void) +svn_fs_bdb__init_cb(void) { - /* We have to initialize the cache exactly once. Because APR - doesn't have statically-initialized mutexes, we implement a poor - man's spinlock using svn__atomic_cas. */ + bdb_cache_pool = svn_pool_create(NULL); + bdb_cache = apr_hash_make(bdb_cache_pool); #if APR_HAS_THREADS - apr_status_t apr_err; - svn__atomic_t cache_state = svn__atomic_cas(&bdb_cache_state, - BDB_CACHE_START_INIT, - BDB_CACHE_UNINITIALIZED); -#endif /* APR_HAS_THREADS */ - -#if APR_HAS_THREADS - if (cache_state == BDB_CACHE_UNINITIALIZED) -#else - if (!bdb_cache_pool) -#endif /* APR_HAS_THREADS */ + apr_status_t apr_err = apr_thread_mutex_create(&bdb_cache_lock, + APR_THREAD_MUTEX_DEFAULT, + bdb_cache_pool); + if (apr_err) { - bdb_cache_pool = svn_pool_create(NULL); - bdb_cache = apr_hash_make(bdb_cache_pool); -#if APR_HAS_THREADS - apr_err = apr_thread_mutex_create(&bdb_cache_lock, - APR_THREAD_MUTEX_DEFAULT, - bdb_cache_pool); - if (apr_err) - { - /* Tell other threads that the initialisation failed. */ - svn__atomic_cas(&bdb_cache_state, - BDB_CACHE_INIT_FAILED, - BDB_CACHE_START_INIT); - return svn_error_create(apr_err, NULL, - "Couldn't initialize the cache of" - " Berkeley DB environment descriptors"); - } - - apr_pool_cleanup_register(bdb_cache_pool, NULL, clear_cache, - apr_pool_cleanup_null); - - svn__atomic_cas(&bdb_cache_state, - BDB_CACHE_INITIALIZED, - BDB_CACHE_START_INIT); -#endif /* APR_HAS_THREADS */ + return svn_error_create(apr_err, NULL, + "Couldn't initialize the cache of" + " Berkeley DB environment descriptors"); } -#if APR_HAS_THREADS - /* Wait for whichever thread is initializing the cache to finish. */ - /* XXX FIXME: Should we have a maximum wait here, like we have in - the Windows file IO spinner? */ - else while (cache_state != BDB_CACHE_INITIALIZED) - { - if (cache_state == BDB_CACHE_INIT_FAILED) - return svn_error_create(SVN_ERR_FS_GENERAL, NULL, - "Couldn't initialize the cache of" - " Berkeley DB environment descriptors"); - - apr_sleep(APR_USEC_PER_SEC / 1000); - cache_state = svn__atomic_cas(&bdb_cache_state, - BDB_CACHE_UNINITIALIZED, - BDB_CACHE_UNINITIALIZED); - } + apr_pool_cleanup_register(bdb_cache_pool, NULL, clear_cache, + apr_pool_cleanup_null); #endif /* APR_HAS_THREADS */ return SVN_NO_ERROR; } +svn_error_t * +svn_fs_bdb__init(void) +{ + SVN_ERR(svn_atomic_init_once(&bdb_cache_state, svn_fs_bdb__init_cb)); + return SVN_NO_ERROR; +} static APR_INLINE void acquire_cache_mutex(void) @@ -516,7 +450,7 @@ bdb_env_t *bdb = apr_hash_get(bdb_cache, keyp, sizeof *keyp); if (bdb && bdb->env) { - *panicp = !!svn__atomic_read(&bdb->panic); + *panicp = !!svn_atomic_read(&bdb->panic); #if SVN_BDB_VERSION_AT_LEAST(4,2) if (!*panicp) { @@ -525,7 +459,7 @@ || (flags & DB_PANIC_ENVIRONMENT)) { /* Something is wrong with the environment. */ - svn__atomic_set(&bdb->panic, TRUE); + svn_atomic_set(&bdb->panic, TRUE); *panicp = TRUE; bdb = NULL; } @@ -598,7 +532,7 @@ /* If the environment is panicked and automatic recovery is not enabled, return an appropriate error. */ - if (!SVN_BDB_AUTO_RECOVER && svn__atomic_read(&bdb->panic)) + if (!SVN_BDB_AUTO_RECOVER && svn_atomic_read(&bdb->panic)) err = svn_error_create(SVN_ERR_FS_BERKELEY_DB, NULL, db_strerror(DB_RUNRECOVERY)); } @@ -763,7 +697,7 @@ return TRUE; assert(bdb_baton->env == bdb_baton->bdb->env); - return !!svn__atomic_read(&bdb_baton->bdb->panic); + return !!svn_atomic_read(&bdb_baton->bdb->panic); } void @@ -773,7 +707,7 @@ return; assert(bdb_baton->env == bdb_baton->bdb->env); - svn__atomic_set(&bdb_baton->bdb->panic, TRUE); + svn_atomic_set(&bdb_baton->bdb->panic, TRUE); } Index: subversion/include/svn_atomic.h =================================================================== --- subversion/include/svn_atomic.h (revision 0) +++ subversion/include/svn_atomic.h (revision 0) @@ -0,0 +1,116 @@ +/** + * @copyright + * ==================================================================== + * Copyright (c) 2006 CollabNet. All rights reserved. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://subversion.tigris.org/license-1.html. + * If newer versions of this license are posted there, you may use a + * newer version instead, at your option. + * + * This software consists of voluntary contributions made by many + * individuals. For exact contribution history, see the revision + * history and logs, available at http://subversion.tigris.org/. + * ==================================================================== + * @endcopyright + * + * @file svn_atomic.h + * @brief Macros and functions for atomic operations + */ + +#ifndef SVN_ATOMIC_H +#define SVN_ATOMIC_H + +#include +#include + +#include "svn_error.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @name Macro definitions for atomic types and operations + * + * @note These are necessary because the apr_atomic API changed somewhat + * between apr-0.x and apr-1.x. + * @{ + */ + +/** The type used by all the other atomic operations. */ +#if APR_MAJOR_VERSION > 0 +#define svn_atomic_t apr_uint32_t +#else +#define svn_atomic_t apr_atomic_t +#endif /* APR_MAJOR_VERSION */ + +/** Atomically read an #svn_atomic_t from memory. */ +#if APR_MAJOR_VERSION > 0 +#define svn_atomic_read(mem) apr_atomic_read32((mem)) +#else +#define svn_atomic_read(mem) apr_atomic_read((mem)) +#endif /* APR_MAJOR_VERSION */ + +/** Atomically set an #svn_atomic_t in memory. */ +#if APR_MAJOR_VERSION > 0 +#define svn_atomic_set(mem, val) apr_atomic_set32((mem), (val)) +#else +#define svn_atomic_set(mem, val) apr_atomic_set((mem), (val)) +#endif /* APR_MAJOR_VERSION */ + +/** + * Atomic compare-and-swap. + * + * Compare the value that @a mem points to with @a cmp. If they are + * the same swap the value with @a with. + * + * @note svn_atomic_cas should not be combined with the other + * svn_atomic operations. A comment in apr_atomic.h explains + * that on some platforms, the CAS function is implemented in a + * way that is incompatible with the other atomic operations. + */ +#if APR_MAJOR_VERSION > 0 +#define svn_atomic_cas(mem, with, cmp) \ + apr_atomic_cas32((mem), (with), (cmp)) +#else +#define svn_atomic_cas(mem, with, cmp) \ + apr_atomic_cas((mem), (with), (cmp)) +#endif /* APR_MAJOR_VERSION */ +/** @} */ + +/** + * @defgroup atomic Atomic initialization + * @{ + */ + +/** + * @name Magic values for atomic initialization + * @{ + */ +#define SVN_UNINITIALIZED 0 +#define SVN_START_INIT 1 +#define SVN_INIT_FAILED 2 +#define SVN_INITIALIZED 3 +/** @} */ + +/** Call an initialization function in a thread-safe manner. + * + * @a global_status must be a pointer to an #svn_atomic_t initialized + * with #SVN_UNINITIALIZED. @a init_func is a pointer to the function that + * performs the actual initialization. + * + * @since New in 1.5. + */ +svn_error_t * +svn_atomic_init_once(volatile svn_atomic_t *global_status, + svn_error_t *(*init_func)(void)); + +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_ATOMIC_H */ Index: subversion/libsvn_subr/atomic.c =================================================================== --- subversion/libsvn_subr/atomic.c (revision 0) +++ subversion/libsvn_subr/atomic.c (revision 0) @@ -0,0 +1,67 @@ +/* atomic.c : perform atomic initialization + * + * ==================================================================== + * Copyright (c) 2000-2005 CollabNet. All rights reserved. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://subversion.tigris.org/license-1.html. + * If newer versions of this license are posted there, you may use a + * newer version instead, at your option. + * + * This software consists of voluntary contributions made by many + * individuals. For exact contribution history, see the revision + * history and logs, available at http://subversion.tigris.org/. + * ==================================================================== + */ + +#include +#include "svn_atomic.h" + +svn_error_t* +svn_atomic_init_once(volatile svn_atomic_t *global_status, + svn_error_t *(*init_func)(void)) +{ + /* We have to call init_func exactly once. Because APR + doesn't have statically-initialized mutexes, we implement a poor + man's spinlock using svn_atomic_cas. */ + svn_atomic_t status = svn_atomic_cas(global_status, + SVN_START_INIT, + SVN_UNINITIALIZED); + + if (status == SVN_UNINITIALIZED) + { + svn_error_t *err = init_func(); + if (err) + { +#ifdef APR_HAS_THREADS + /* Tell other threads that the initialization failed. */ + svn_atomic_cas(global_status, + SVN_INIT_FAILED, + SVN_START_INIT); +#endif + return err; + } + svn_atomic_cas(global_status, + SVN_INITIALIZED, + SVN_START_INIT); + } +#ifdef APR_HAS_THREADS + /* Wait for whichever thread is performing initialization to finish. */ + /* XXX FIXME: Should we have a maximum wait here, like we have in + the Windows file IO spinner? */ + else while (status != SVN_INITIALIZED) + { + if (status == SVN_INIT_FAILED) + return svn_error_create(SVN_ERR_FS_GENERAL, NULL, + "Couldn't perform atomic initialization"); + + apr_sleep(APR_USEC_PER_SEC / 1000); + status = svn_atomic_cas(global_status, + SVN_UNINITIALIZED, + SVN_UNINITIALIZED); + } +#endif /* APR_HAS_THREADS */ + + return SVN_NO_ERROR; +}