diff options
Diffstat (limited to 'subversion/libsvn_subr/atomic.c')
-rw-r--r-- | subversion/libsvn_subr/atomic.c | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/subversion/libsvn_subr/atomic.c b/subversion/libsvn_subr/atomic.c new file mode 100644 index 0000000..b760da4 --- /dev/null +++ b/subversion/libsvn_subr/atomic.c @@ -0,0 +1,85 @@ +/* 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; +} |