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