xref: /f-stack/app/redis-5.0.5/src/atomicvar.h (revision 572c4311)
1 /* This file implements atomic counters using __atomic or __sync macros if
2  * available, otherwise synchronizing different threads using a mutex.
3  *
4  * The exported interface is composed of three macros:
5  *
6  * atomicIncr(var,count) -- Increment the atomic counter
7  * atomicGetIncr(var,oldvalue_var,count) -- Get and increment the atomic counter
8  * atomicDecr(var,count) -- Decrement the atomic counter
9  * atomicGet(var,dstvar) -- Fetch the atomic counter value
10  * atomicSet(var,value)  -- Set the atomic counter value
11  *
12  * The variable 'var' should also have a declared mutex with the same
13  * name and the "_mutex" postfix, for instance:
14  *
15  *  long myvar;
16  *  pthread_mutex_t myvar_mutex;
17  *  atomicSet(myvar,12345);
18  *
19  * If atomic primitives are available (tested in config.h) the mutex
20  * is not used.
21  *
22  * Never use return value from the macros, instead use the AtomicGetIncr()
23  * if you need to get the current value and increment it atomically, like
24  * in the followign example:
25  *
26  *  long oldvalue;
27  *  atomicGetIncr(myvar,oldvalue,1);
28  *  doSomethingWith(oldvalue);
29  *
30  * ----------------------------------------------------------------------------
31  *
32  * Copyright (c) 2015, Salvatore Sanfilippo <antirez at gmail dot com>
33  * All rights reserved.
34  *
35  * Redistribution and use in source and binary forms, with or without
36  * modification, are permitted provided that the following conditions are met:
37  *
38  *   * Redistributions of source code must retain the above copyright notice,
39  *     this list of conditions and the following disclaimer.
40  *   * Redistributions in binary form must reproduce the above copyright
41  *     notice, this list of conditions and the following disclaimer in the
42  *     documentation and/or other materials provided with the distribution.
43  *   * Neither the name of Redis nor the names of its contributors may be used
44  *     to endorse or promote products derived from this software without
45  *     specific prior written permission.
46  *
47  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
48  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
51  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
52  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
53  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
54  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
55  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
56  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
57  * POSSIBILITY OF SUCH DAMAGE.
58  */
59 
60 #include <pthread.h>
61 
62 #ifndef __ATOMIC_VAR_H
63 #define __ATOMIC_VAR_H
64 
65 /* To test Redis with Helgrind (a Valgrind tool) it is useful to define
66  * the following macro, so that __sync macros are used: those can be detected
67  * by Helgrind (even if they are less efficient) so that no false positive
68  * is reported. */
69 // #define __ATOMIC_VAR_FORCE_SYNC_MACROS
70 
71 #if !defined(__ATOMIC_VAR_FORCE_SYNC_MACROS) && defined(__ATOMIC_RELAXED) && !defined(__sun) && (!defined(__clang__) || !defined(__APPLE__) || __apple_build_version__ > 4210057)
72 /* Implementation using __atomic macros. */
73 
74 #define atomicIncr(var,count) __atomic_add_fetch(&var,(count),__ATOMIC_RELAXED)
75 #define atomicGetIncr(var,oldvalue_var,count) do { \
76     oldvalue_var = __atomic_fetch_add(&var,(count),__ATOMIC_RELAXED); \
77 } while(0)
78 #define atomicDecr(var,count) __atomic_sub_fetch(&var,(count),__ATOMIC_RELAXED)
79 #define atomicGet(var,dstvar) do { \
80     dstvar = __atomic_load_n(&var,__ATOMIC_RELAXED); \
81 } while(0)
82 #define atomicSet(var,value) __atomic_store_n(&var,value,__ATOMIC_RELAXED)
83 #define REDIS_ATOMIC_API "atomic-builtin"
84 
85 #elif defined(HAVE_ATOMIC)
86 /* Implementation using __sync macros. */
87 
88 #define atomicIncr(var,count) __sync_add_and_fetch(&var,(count))
89 #define atomicGetIncr(var,oldvalue_var,count) do { \
90     oldvalue_var = __sync_fetch_and_add(&var,(count)); \
91 } while(0)
92 #define atomicDecr(var,count) __sync_sub_and_fetch(&var,(count))
93 #define atomicGet(var,dstvar) do { \
94     dstvar = __sync_sub_and_fetch(&var,0); \
95 } while(0)
96 #define atomicSet(var,value) do { \
97     while(!__sync_bool_compare_and_swap(&var,var,value)); \
98 } while(0)
99 #define REDIS_ATOMIC_API "sync-builtin"
100 
101 #else
102 /* Implementation using pthread mutex. */
103 
104 #define atomicIncr(var,count) do { \
105     pthread_mutex_lock(&var ## _mutex); \
106     var += (count); \
107     pthread_mutex_unlock(&var ## _mutex); \
108 } while(0)
109 #define atomicGetIncr(var,oldvalue_var,count) do { \
110     pthread_mutex_lock(&var ## _mutex); \
111     oldvalue_var = var; \
112     var += (count); \
113     pthread_mutex_unlock(&var ## _mutex); \
114 } while(0)
115 #define atomicDecr(var,count) do { \
116     pthread_mutex_lock(&var ## _mutex); \
117     var -= (count); \
118     pthread_mutex_unlock(&var ## _mutex); \
119 } while(0)
120 #define atomicGet(var,dstvar) do { \
121     pthread_mutex_lock(&var ## _mutex); \
122     dstvar = var; \
123     pthread_mutex_unlock(&var ## _mutex); \
124 } while(0)
125 #define atomicSet(var,value) do { \
126     pthread_mutex_lock(&var ## _mutex); \
127     var = value; \
128     pthread_mutex_unlock(&var ## _mutex); \
129 } while(0)
130 #define REDIS_ATOMIC_API "pthread-mutex"
131 
132 #endif
133 #endif /* __ATOMIC_VAR_H */
134