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