blob: b760da4fe156d539c76a20d44667ffb2f0f6ba06 [file] [log] [blame]
/* atomic.c : perform atomic initialization
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#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;
}