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 <assert.h> 24#include <apr_time.h> 25 26#include "svn_pools.h" 27 28#include "private/svn_atomic.h" 29#include "private/svn_mutex.h" 30 31/* Magic values for atomic initialization */ 32#define SVN_ATOMIC_UNINITIALIZED 0 33#define SVN_ATOMIC_START_INIT 1 34#define SVN_ATOMIC_INIT_FAILED 2 35#define SVN_ATOMIC_INITIALIZED 3 36 37 38/* Baton used by init_funct_t and init_once(). */ 39typedef struct init_baton_t init_baton_t; 40 41/* Initialization function wrapper. Hides API details from init_once(). 42 The implementation must return FALSE on failure. */ 43typedef svn_boolean_t (*init_func_t)(init_baton_t *init_baton); 44 45/* 46 * This is the actual atomic initialization driver. 47 * Returns FALSE on failure. 48 */ 49static svn_boolean_t 50init_once(volatile svn_atomic_t *global_status, 51 init_func_t init_func, init_baton_t *init_baton) 52{ 53 /* !! Don't use localizable strings in this function, because these 54 !! might cause deadlocks. This function can be used to initialize 55 !! libraries that are used for generating error messages. */ 56 57 /* We have to call init_func exactly once. Because APR 58 doesn't have statically-initialized mutexes, we implement a poor 59 man's spinlock using svn_atomic_cas. */ 60 61 svn_atomic_t status = svn_atomic_cas(global_status, 62 SVN_ATOMIC_START_INIT, 63 SVN_ATOMIC_UNINITIALIZED); 64 65 for (;;) 66 { 67 switch (status) 68 { 69 case SVN_ATOMIC_UNINITIALIZED: 70 { 71 const svn_boolean_t result = init_func(init_baton); 72 const svn_atomic_t init_state = (result 73 ? SVN_ATOMIC_INITIALIZED 74 : SVN_ATOMIC_INIT_FAILED); 75 76 svn_atomic_cas(global_status, init_state, 77 SVN_ATOMIC_START_INIT); 78 return result; 79 } 80 81 case SVN_ATOMIC_START_INIT: 82 /* Wait for the init function to complete. */ 83 apr_sleep(APR_USEC_PER_SEC / 1000); 84 status = svn_atomic_cas(global_status, 85 SVN_ATOMIC_UNINITIALIZED, 86 SVN_ATOMIC_UNINITIALIZED); 87 continue; 88 89 case SVN_ATOMIC_INIT_FAILED: 90 return FALSE; 91 92 case SVN_ATOMIC_INITIALIZED: 93 return TRUE; 94 95 default: 96 /* Something went seriously wrong with the atomic operations. */ 97 abort(); 98 } 99 } 100} 101 102 103/* This baton structure is used by the two flavours of init-once APIs 104 to hide their differences from the init_once() driver. Each private 105 API uses only selected parts of the baton. 106 107 No part of this structure changes unless a wrapped init function is 108 actually invoked by init_once(). 109*/ 110struct init_baton_t 111{ 112 /* Used only by svn_atomic__init_once()/err_init_func_wrapper() */ 113 svn_atomic__err_init_func_t err_init_func; 114 svn_error_t *err; 115 apr_pool_t *pool; 116 117 /* Used only by svn_atomic__init_no_error()/str_init_func_wrapper() */ 118 svn_atomic__str_init_func_t str_init_func; 119 const char *errstr; 120 121 /* Used by both pairs of functions */ 122 void *baton; 123}; 124 125/* Wrapper for the svn_atomic__init_once init function. */ 126static svn_boolean_t err_init_func_wrapper(init_baton_t *init_baton) 127{ 128 init_baton->err = init_baton->err_init_func(init_baton->baton, 129 init_baton->pool); 130 return (init_baton->err == SVN_NO_ERROR); 131} 132 133svn_error_t * 134svn_atomic__init_once(volatile svn_atomic_t *global_status, 135 svn_atomic__err_init_func_t err_init_func, 136 void *baton, 137 apr_pool_t* pool) 138{ 139 init_baton_t init_baton; 140 init_baton.err_init_func = err_init_func; 141 init_baton.err = NULL; 142 init_baton.pool = pool; 143 init_baton.baton = baton; 144 145 if (init_once(global_status, err_init_func_wrapper, &init_baton)) 146 return SVN_NO_ERROR; 147 148 return svn_error_create(SVN_ERR_ATOMIC_INIT_FAILURE, init_baton.err, 149 "Couldn't perform atomic initialization"); 150} 151 152 153/* Wrapper for the svn_atomic__init_no_error init function. */ 154static svn_boolean_t str_init_func_wrapper(init_baton_t *init_baton) 155{ 156 init_baton->errstr = init_baton->str_init_func(init_baton->baton); 157 return (init_baton->errstr == NULL); 158} 159 160const char * 161svn_atomic__init_once_no_error(volatile svn_atomic_t *global_status, 162 svn_atomic__str_init_func_t str_init_func, 163 void *baton) 164{ 165 init_baton_t init_baton; 166 init_baton.str_init_func = str_init_func; 167 init_baton.errstr = NULL; 168 init_baton.baton = baton; 169 170 if (init_once(global_status, str_init_func_wrapper, &init_baton)) 171 return NULL; 172 173 /* Our init function wrapper may not have been called; make sure 174 that we return generic error message in that case. */ 175 if (!init_baton.errstr) 176 return "Couldn't perform atomic initialization"; 177 else 178 return init_baton.errstr; 179} 180 181/* The process-global counter that we use to produce process-wide unique 182 * values. Since APR has no 64 bit atomics, all access to this will be 183 * serialized through COUNTER_MUTEX. */ 184static apr_uint64_t uniqiue_counter = 0; 185 186/* The corresponding mutex and initialization state. */ 187static volatile svn_atomic_t counter_status = SVN_ATOMIC_UNINITIALIZED; 188static svn_mutex__t *counter_mutex = NULL; 189 190/* svn_atomic__err_init_func_t implementation that initializes COUNTER_MUTEX. 191 * Note that neither argument will be used and should be NULL. */ 192static svn_error_t * 193init_unique_counter(void *null_baton, 194 apr_pool_t *null_pool) 195{ 196 /* COUNTER_MUTEX is global, so it needs to live in a global pool. 197 * APR also makes those thread-safe by default. */ 198 SVN_ERR(svn_mutex__init(&counter_mutex, TRUE, svn_pool_create(NULL))); 199 return SVN_NO_ERROR; 200} 201 202/* Read and increment UNIQIUE_COUNTER. Return the new value in *VALUE. 203 * Call this function only while having acquired the COUNTER_MUTEX. */ 204static svn_error_t * 205read_unique_counter(apr_uint64_t *value) 206{ 207 *value = ++uniqiue_counter; 208 return SVN_NO_ERROR; 209} 210 211svn_error_t * 212svn_atomic__unique_counter(apr_uint64_t *value) 213{ 214 SVN_ERR(svn_atomic__init_once(&counter_status, init_unique_counter, NULL, 215 NULL)); 216 SVN_MUTEX__WITH_LOCK(counter_mutex, read_unique_counter(value)); 217 return SVN_NO_ERROR; 218} 219