/* 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 <apr_time.h>
#include "private/svn_atomic.h"
/* Magic values for atomic initialization */
#define SVN_ATOMIC_UNINITIALIZED 0
#define SVN_ATOMIC_START_INIT 1
#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)(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
!! libraries that are used for generating error messages. */
/* 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_ATOMIC_START_INIT,
SVN_ATOMIC_UNINITIALIZED);
if (status == SVN_ATOMIC_UNINITIALIZED)
{
svn_error_t *err = init_func(baton, pool);
if (err)
{
#if APR_HAS_THREADS
/* Tell other threads that the initialization failed. */
svn_atomic_cas(global_status,
SVN_ATOMIC_INIT_FAILED,
SVN_ATOMIC_START_INIT);
#endif
return svn_error_create(SVN_ERR_ATOMIC_INIT_FAILURE, err,
"Couldn't perform atomic initialization");
}
svn_atomic_cas(global_status,
SVN_ATOMIC_INITIALIZED,
SVN_ATOMIC_START_INIT);
}
#if 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_ATOMIC_INITIALIZED)
{
if (status == SVN_ATOMIC_INIT_FAILED)
return svn_error_create(SVN_ERR_ATOMIC_INIT_FAILURE, NULL,
"Couldn't perform atomic initialization");
apr_sleep(APR_USEC_PER_SEC / 1000);
status = svn_atomic_cas(global_status,
SVN_ATOMIC_UNINITIALIZED,
SVN_ATOMIC_UNINITIALIZED);
}
#endif /* APR_HAS_THREADS */
return SVN_NO_ERROR;
}