1/* atomic.c : perform atomic initialization 2 * 3 * ==================================================================== 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 * ==================================================================== 21 */ 22 23#include <apr_time.h> 24#include "private/svn_atomic.h" 25 26/* Magic values for atomic initialization */ 27#define SVN_ATOMIC_UNINITIALIZED 0 28#define SVN_ATOMIC_START_INIT 1 29#define SVN_ATOMIC_INIT_FAILED 2 30#define SVN_ATOMIC_INITIALIZED 3 31 32svn_error_t* 33svn_atomic__init_once(volatile svn_atomic_t *global_status, 34 svn_error_t *(*init_func)(void*,apr_pool_t*), 35 void *baton, 36 apr_pool_t* pool) 37{ 38 /* !! Don't use localizable strings in this function, because these 39 !! might cause deadlocks. This function can be used to initialize 40 !! libraries that are used for generating error messages. */ 41 42 /* We have to call init_func exactly once. Because APR 43 doesn't have statically-initialized mutexes, we implement a poor 44 man's spinlock using svn_atomic_cas. */ 45 svn_atomic_t status = svn_atomic_cas(global_status, 46 SVN_ATOMIC_START_INIT, 47 SVN_ATOMIC_UNINITIALIZED); 48 49 if (status == SVN_ATOMIC_UNINITIALIZED) 50 { 51 svn_error_t *err = init_func(baton, pool); 52 if (err) 53 { 54#if APR_HAS_THREADS 55 /* Tell other threads that the initialization failed. */ 56 svn_atomic_cas(global_status, 57 SVN_ATOMIC_INIT_FAILED, 58 SVN_ATOMIC_START_INIT); 59#endif 60 return svn_error_create(SVN_ERR_ATOMIC_INIT_FAILURE, err, 61 "Couldn't perform atomic initialization"); 62 } 63 svn_atomic_cas(global_status, 64 SVN_ATOMIC_INITIALIZED, 65 SVN_ATOMIC_START_INIT); 66 } 67#if APR_HAS_THREADS 68 /* Wait for whichever thread is performing initialization to finish. */ 69 /* XXX FIXME: Should we have a maximum wait here, like we have in 70 the Windows file IO spinner? */ 71 else while (status != SVN_ATOMIC_INITIALIZED) 72 { 73 if (status == SVN_ATOMIC_INIT_FAILED) 74 return svn_error_create(SVN_ERR_ATOMIC_INIT_FAILURE, NULL, 75 "Couldn't perform atomic initialization"); 76 77 apr_sleep(APR_USEC_PER_SEC / 1000); 78 status = svn_atomic_cas(global_status, 79 SVN_ATOMIC_UNINITIALIZED, 80 SVN_ATOMIC_UNINITIALIZED); 81 } 82#endif /* APR_HAS_THREADS */ 83 84 return SVN_NO_ERROR; 85} 86