1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "apr_arch_atomic.h"
18
19#ifdef USE_ATOMICS_GENERIC
20
21#include <stdlib.h>
22
23#if APR_HAS_THREADS
24#   define DECLARE_MUTEX_LOCKED(name, mem)  \
25        apr_thread_mutex_t *name = mutex_hash(mem)
26#   define MUTEX_UNLOCK(name)                                   \
27        do {                                                    \
28            if (apr_thread_mutex_unlock(name) != APR_SUCCESS)   \
29                abort();                                        \
30        } while (0)
31#else
32#   define DECLARE_MUTEX_LOCKED(name, mem)
33#   define MUTEX_UNLOCK(name)
34#   warning Be warned: using stubs for all atomic operations
35#endif
36
37#if APR_HAS_THREADS
38
39static apr_thread_mutex_t **hash_mutex;
40
41#define NUM_ATOMIC_HASH 7
42/* shift by 2 to get rid of alignment issues */
43#define ATOMIC_HASH(x) (unsigned int)(((unsigned long)(x)>>2)%(unsigned int)NUM_ATOMIC_HASH)
44
45static apr_status_t atomic_cleanup(void *data)
46{
47    if (hash_mutex == data)
48        hash_mutex = NULL;
49
50    return APR_SUCCESS;
51}
52
53APR_DECLARE(apr_status_t) apr_atomic_init(apr_pool_t *p)
54{
55    int i;
56    apr_status_t rv;
57
58    if (hash_mutex != NULL)
59        return APR_SUCCESS;
60
61    hash_mutex = apr_palloc(p, sizeof(apr_thread_mutex_t*) * NUM_ATOMIC_HASH);
62    apr_pool_cleanup_register(p, hash_mutex, atomic_cleanup,
63                              apr_pool_cleanup_null);
64
65    for (i = 0; i < NUM_ATOMIC_HASH; i++) {
66        rv = apr_thread_mutex_create(&(hash_mutex[i]),
67                                     APR_THREAD_MUTEX_DEFAULT, p);
68        if (rv != APR_SUCCESS) {
69           return rv;
70        }
71    }
72
73    return APR_SUCCESS;
74}
75
76static APR_INLINE apr_thread_mutex_t *mutex_hash(volatile apr_uint32_t *mem)
77{
78    apr_thread_mutex_t *mutex = hash_mutex[ATOMIC_HASH(mem)];
79
80    if (apr_thread_mutex_lock(mutex) != APR_SUCCESS) {
81        abort();
82    }
83
84    return mutex;
85}
86
87#else
88
89APR_DECLARE(apr_status_t) apr_atomic_init(apr_pool_t *p)
90{
91    return APR_SUCCESS;
92}
93
94#endif /* APR_HAS_THREADS */
95
96APR_DECLARE(apr_uint32_t) apr_atomic_read32(volatile apr_uint32_t *mem)
97{
98    return *mem;
99}
100
101APR_DECLARE(void) apr_atomic_set32(volatile apr_uint32_t *mem, apr_uint32_t val)
102{
103    DECLARE_MUTEX_LOCKED(mutex, mem);
104
105    *mem = val;
106
107    MUTEX_UNLOCK(mutex);
108}
109
110APR_DECLARE(apr_uint32_t) apr_atomic_add32(volatile apr_uint32_t *mem, apr_uint32_t val)
111{
112    apr_uint32_t old_value;
113    DECLARE_MUTEX_LOCKED(mutex, mem);
114
115    old_value = *mem;
116    *mem += val;
117
118    MUTEX_UNLOCK(mutex);
119
120    return old_value;
121}
122
123APR_DECLARE(void) apr_atomic_sub32(volatile apr_uint32_t *mem, apr_uint32_t val)
124{
125    DECLARE_MUTEX_LOCKED(mutex, mem);
126    *mem -= val;
127    MUTEX_UNLOCK(mutex);
128}
129
130APR_DECLARE(apr_uint32_t) apr_atomic_inc32(volatile apr_uint32_t *mem)
131{
132    return apr_atomic_add32(mem, 1);
133}
134
135APR_DECLARE(int) apr_atomic_dec32(volatile apr_uint32_t *mem)
136{
137    apr_uint32_t new;
138    DECLARE_MUTEX_LOCKED(mutex, mem);
139
140    (*mem)--;
141    new = *mem;
142
143    MUTEX_UNLOCK(mutex);
144
145    return new;
146}
147
148APR_DECLARE(apr_uint32_t) apr_atomic_cas32(volatile apr_uint32_t *mem, apr_uint32_t with,
149                              apr_uint32_t cmp)
150{
151    apr_uint32_t prev;
152    DECLARE_MUTEX_LOCKED(mutex, mem);
153
154    prev = *mem;
155    if (prev == cmp) {
156        *mem = with;
157    }
158
159    MUTEX_UNLOCK(mutex);
160
161    return prev;
162}
163
164APR_DECLARE(apr_uint32_t) apr_atomic_xchg32(volatile apr_uint32_t *mem, apr_uint32_t val)
165{
166    apr_uint32_t prev;
167    DECLARE_MUTEX_LOCKED(mutex, mem);
168
169    prev = *mem;
170    *mem = val;
171
172    MUTEX_UNLOCK(mutex);
173
174    return prev;
175}
176
177APR_DECLARE(void*) apr_atomic_casptr(volatile void **mem, void *with, const void *cmp)
178{
179    void *prev;
180    DECLARE_MUTEX_LOCKED(mutex, *mem);
181
182    prev = *(void **)mem;
183    if (prev == cmp) {
184        *mem = with;
185    }
186
187    MUTEX_UNLOCK(mutex);
188
189    return prev;
190}
191
192APR_DECLARE(void*) apr_atomic_xchgptr(volatile void **mem, void *with)
193{
194    void *prev;
195    DECLARE_MUTEX_LOCKED(mutex, *mem);
196
197    prev = *(void **)mem;
198    *mem = with;
199
200    MUTEX_UNLOCK(mutex);
201
202    return prev;
203}
204
205#endif /* USE_ATOMICS_GENERIC */
206