1 /* 2 * Copyright 2010-2015 Samy Al Bahra. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #ifndef CK_SPINLOCK_DEC_H 28 #define CK_SPINLOCK_DEC_H 29 30 #include <ck_backoff.h> 31 #include <ck_cc.h> 32 #include <ck_elide.h> 33 #include <ck_pr.h> 34 #include <ck_stdbool.h> 35 36 #ifndef CK_F_SPINLOCK_DEC 37 #define CK_F_SPINLOCK_DEC 38 /* 39 * This is similar to the CACAS lock but makes use of an atomic decrement 40 * operation to check if the lock value was decremented to 0 from 1. The 41 * idea is that a decrement operation is cheaper than a compare-and-swap. 42 */ 43 struct ck_spinlock_dec { 44 unsigned int value; 45 }; 46 typedef struct ck_spinlock_dec ck_spinlock_dec_t; 47 48 #define CK_SPINLOCK_DEC_INITIALIZER {1} 49 50 CK_CC_INLINE static void 51 ck_spinlock_dec_init(struct ck_spinlock_dec *lock) 52 { 53 54 lock->value = 1; 55 ck_pr_barrier(); 56 return; 57 } 58 59 CK_CC_INLINE static bool 60 ck_spinlock_dec_trylock(struct ck_spinlock_dec *lock) 61 { 62 unsigned int value; 63 64 value = ck_pr_fas_uint(&lock->value, 0); 65 ck_pr_fence_lock(); 66 return value == 1; 67 } 68 69 CK_CC_INLINE static bool 70 ck_spinlock_dec_locked(struct ck_spinlock_dec *lock) 71 { 72 bool r; 73 74 r = ck_pr_load_uint(&lock->value) != 1; 75 ck_pr_fence_acquire(); 76 return r; 77 } 78 79 CK_CC_INLINE static void 80 ck_spinlock_dec_lock(struct ck_spinlock_dec *lock) 81 { 82 bool r; 83 84 for (;;) { 85 /* 86 * Only one thread is guaranteed to decrement lock to 0. 87 * Overflow must be protected against. No more than 88 * UINT_MAX lock requests can happen while the lock is held. 89 */ 90 ck_pr_dec_uint_zero(&lock->value, &r); 91 if (r == true) 92 break; 93 94 /* Load value without generating write cycles. */ 95 while (ck_pr_load_uint(&lock->value) != 1) 96 ck_pr_stall(); 97 } 98 99 ck_pr_fence_lock(); 100 return; 101 } 102 103 CK_CC_INLINE static void 104 ck_spinlock_dec_lock_eb(struct ck_spinlock_dec *lock) 105 { 106 ck_backoff_t backoff = CK_BACKOFF_INITIALIZER; 107 bool r; 108 109 for (;;) { 110 ck_pr_dec_uint_zero(&lock->value, &r); 111 if (r == true) 112 break; 113 114 while (ck_pr_load_uint(&lock->value) != 1) 115 ck_backoff_eb(&backoff); 116 } 117 118 ck_pr_fence_lock(); 119 return; 120 } 121 122 CK_CC_INLINE static void 123 ck_spinlock_dec_unlock(struct ck_spinlock_dec *lock) 124 { 125 126 ck_pr_fence_unlock(); 127 128 /* 129 * Unconditionally set lock value to 1 so someone can decrement lock 130 * to 0. 131 */ 132 ck_pr_store_uint(&lock->value, 1); 133 return; 134 } 135 136 CK_ELIDE_PROTOTYPE(ck_spinlock_dec, ck_spinlock_dec_t, 137 ck_spinlock_dec_locked, ck_spinlock_dec_lock, 138 ck_spinlock_dec_locked, ck_spinlock_dec_unlock) 139 140 CK_ELIDE_TRYLOCK_PROTOTYPE(ck_spinlock_dec, ck_spinlock_dec_t, 141 ck_spinlock_dec_locked, ck_spinlock_dec_trylock) 142 143 #endif /* CK_F_SPINLOCK_DEC */ 144 #endif /* CK_SPINLOCK_DEC_H */ 145