Philip Martin wrote on Wed, 14 Apr 2010 at 12:17 +0100: > -svn_error_t * > -svn_fs_fs__open_rep_cache(svn_fs_t *fs, > - apr_pool_t *pool) > +static svn_error_t* > +open_rep_cache(fs_fs_data_t *ffd, > + const char *fs_path, > + apr_pool_t *fs_pool, > + apr_pool_t *scratch_pool) > { > - fs_fs_data_t *ffd = fs->fsap_data; > const char *db_path; > int version; > > - /* Be idempotent. */ > + /* Be idempotent. ### This is not thread safe. */ > if (ffd->rep_cache_db) > return SVN_NO_ERROR; >
I suppose we could fix the thread-unsafety while we're here. Something like this? [[[ In FSFS, open the FS object's rep-cache atomically. Extend fs_fs_data_t and svn_atomic__init_once() to support this. (Yes, as it stands, this patch actually rev's a private API. This is for presentational purposes; I'll do the "rev all callers" dance when it's committed.) Index: subversion/include/private/svn_atomic.h =================================================================== --- subversion/include/private/svn_atomic.h (revision 934086) +++ subversion/include/private/svn_atomic.h (working copy) @@ -106,8 +106,20 @@ extern "C" { * @a global_status must be a pointer to a global, zero-initialized * #svn_atomic_t. @a init_func is a pointer to the function that performs * the actual initialization, and @a pool is passed on to the init_func - * for its use. + * for its use. @a baton will be passed to @a init_func. (In most uses, + * @a baton will be @c NULL.) * + * @since New in 1.7. + */ +svn_error_t * +svn_atomic__init_once2(volatile svn_atomic_t *global_status, + svn_error_t *(*init_func)(void*,apr_pool_t*), + void *baton, + apr_pool_t* pool); + +/** + * Similar to svn_atomic__init_once2(), but without a @a baton. + * * @since New in 1.5. */ svn_error_t * Index: subversion/libsvn_fs_fs/fs.h =================================================================== --- subversion/libsvn_fs_fs/fs.h (revision 934086) +++ subversion/libsvn_fs_fs/fs.h (working copy) @@ -30,6 +30,7 @@ #include "svn_fs.h" #include "svn_config.h" +#include "private/svn_atomic.h" #include "private/svn_cache.h" #include "private/svn_fs_private.h" #include "private/svn_sqlite.h" @@ -245,6 +246,9 @@ typedef struct /* The sqlite database used for rep caching. */ svn_sqlite__db_t *rep_cache_db; + /* Thread-safe boolean */ + svn_atomic_t rep_cache_db_opened; + /* The sqlite database used for revprops. */ svn_sqlite__db_t *revprop_db; Index: subversion/libsvn_fs_fs/rep-cache.c =================================================================== --- subversion/libsvn_fs_fs/rep-cache.c (revision 934086) +++ subversion/libsvn_fs_fs/rep-cache.c (working copy) @@ -38,18 +38,15 @@ REP_CACHE_DB_SQL_DECLARE_STATEMENTS(statements); -svn_error_t * -svn_fs_fs__open_rep_cache(svn_fs_t *fs, - apr_pool_t *pool) +static svn_error_t * +open_rep_cache(void *baton, + apr_pool_t *pool) { + svn_fs_t *fs = baton; fs_fs_data_t *ffd = fs->fsap_data; const char *db_path; int version; - /* Be idempotent. */ - if (ffd->rep_cache_db) - return SVN_NO_ERROR; - /* Open (or create) the sqlite database. It will be automatically closed when fs->pool is destoyed. */ db_path = svn_dirent_join(fs->path, REP_CACHE_DB_NAME, pool); @@ -71,6 +68,15 @@ REP_CACHE_DB_SQL_DECLARE_STATEMENTS(statements); } svn_error_t * +svn_fs_fs__open_rep_cache(svn_fs_t *fs, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + return svn_error_return(svn_atomic__init_once2(&ffd->rep_cache_db_opened, + open_rep_cache, fs, pool)); +} + +svn_error_t * svn_fs_fs__get_rep_reference(representation_t **rep, svn_fs_t *fs, svn_checksum_t *checksum, Index: subversion/libsvn_subr/atomic.c =================================================================== --- subversion/libsvn_subr/atomic.c (revision 934086) +++ subversion/libsvn_subr/atomic.c (working copy) @@ -29,9 +29,11 @@ #define SVN_ATOMIC_INIT_FAILED 2 #define SVN_ATOMIC_INITIALIZED 3 -svn_error_t* -svn_atomic__init_once(volatile svn_atomic_t *global_status, - svn_error_t *(*init_func)(apr_pool_t*), apr_pool_t* pool) +svn_error_t * +svn_atomic__init_once2(volatile svn_atomic_t *global_status, + svn_error_t *(*init_func)(void*,apr_pool_t*), + void *baton, + apr_pool_t* pool) { /* !! Don't use localizable strings in this function, because these !! might cause deadlocks. This function can be used to initialize @@ -46,7 +48,7 @@ if (status == SVN_ATOMIC_UNINITIALIZED) { - svn_error_t *err = init_func(pool); + svn_error_t *err = init_func(baton, pool); if (err) { #if APR_HAS_THREADS @@ -81,3 +83,20 @@ return SVN_NO_ERROR; } + +static svn_error_t * +wrap_init_func(void *baton, apr_pool_t *pool) +{ + svn_error_t *(*wrapped_init_func)(apr_pool_t*) = baton; + return svn_error_return(wrapped_init_func(pool)); +} + +svn_error_t * +svn_atomic__init_once(volatile svn_atomic_t *global_status, + svn_error_t *(*init_func)(apr_pool_t*), apr_pool_t* pool) +{ + return svn_error_return( + svn_atomic__init_once2(global_status, + wrap_init_func, init_func, + pool)); +} ]]] Daniel > /* Open (or create) the sqlite database. It will be automatically > closed when fs->pool is destoyed. */ > - db_path = svn_dirent_join(fs->path, REP_CACHE_DB_NAME, pool); > + db_path = svn_dirent_join(fs_path, REP_CACHE_DB_NAME, scratch_pool); > SVN_ERR(svn_sqlite__open(&ffd->rep_cache_db, db_path, > svn_sqlite__mode_rwcreate, statements, > 0, NULL, > - fs->pool, pool)); > + fs_pool, scratch_pool)); > > - SVN_ERR(svn_sqlite__read_schema_version(&version, ffd->rep_cache_db, > pool)); > + SVN_ERR(svn_sqlite__read_schema_version(&version, ffd->rep_cache_db, > + scratch_pool)); > + /* ### This is not thread safe. */ > if (version < REP_CACHE_SCHEMA_FORMAT) > { > /* Must be 0 -- an uninitialized (no schema) database. Create