1*1fb62fb0SOlivier Houchard /* 2*1fb62fb0SOlivier Houchard * Copyright 2010-2015 Samy Al Bahra. 3*1fb62fb0SOlivier Houchard * All rights reserved. 4*1fb62fb0SOlivier Houchard * 5*1fb62fb0SOlivier Houchard * Redistribution and use in source and binary forms, with or without 6*1fb62fb0SOlivier Houchard * modification, are permitted provided that the following conditions 7*1fb62fb0SOlivier Houchard * are met: 8*1fb62fb0SOlivier Houchard * 1. Redistributions of source code must retain the above copyright 9*1fb62fb0SOlivier Houchard * notice, this list of conditions and the following disclaimer. 10*1fb62fb0SOlivier Houchard * 2. Redistributions in binary form must reproduce the above copyright 11*1fb62fb0SOlivier Houchard * notice, this list of conditions and the following disclaimer in the 12*1fb62fb0SOlivier Houchard * documentation and/or other materials provided with the distribution. 13*1fb62fb0SOlivier Houchard * 14*1fb62fb0SOlivier Houchard * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15*1fb62fb0SOlivier Houchard * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*1fb62fb0SOlivier Houchard * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*1fb62fb0SOlivier Houchard * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18*1fb62fb0SOlivier Houchard * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*1fb62fb0SOlivier Houchard * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*1fb62fb0SOlivier Houchard * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*1fb62fb0SOlivier Houchard * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*1fb62fb0SOlivier Houchard * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*1fb62fb0SOlivier Houchard * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*1fb62fb0SOlivier Houchard * SUCH DAMAGE. 25*1fb62fb0SOlivier Houchard */ 26*1fb62fb0SOlivier Houchard 27*1fb62fb0SOlivier Houchard #ifndef CK_SPINLOCK_ANDERSON_H 28*1fb62fb0SOlivier Houchard #define CK_SPINLOCK_ANDERSON_H 29*1fb62fb0SOlivier Houchard 30*1fb62fb0SOlivier Houchard #include <ck_cc.h> 31*1fb62fb0SOlivier Houchard #include <ck_limits.h> 32*1fb62fb0SOlivier Houchard #include <ck_md.h> 33*1fb62fb0SOlivier Houchard #include <ck_pr.h> 34*1fb62fb0SOlivier Houchard #include <ck_stdbool.h> 35*1fb62fb0SOlivier Houchard 36*1fb62fb0SOlivier Houchard #ifndef CK_F_SPINLOCK_ANDERSON 37*1fb62fb0SOlivier Houchard #define CK_F_SPINLOCK_ANDERSON 38*1fb62fb0SOlivier Houchard /* 39*1fb62fb0SOlivier Houchard * This is an implementation of Anderson's array-based queuing lock. 40*1fb62fb0SOlivier Houchard */ 41*1fb62fb0SOlivier Houchard struct ck_spinlock_anderson_thread { 42*1fb62fb0SOlivier Houchard unsigned int locked; 43*1fb62fb0SOlivier Houchard unsigned int position; 44*1fb62fb0SOlivier Houchard }; 45*1fb62fb0SOlivier Houchard typedef struct ck_spinlock_anderson_thread ck_spinlock_anderson_thread_t; 46*1fb62fb0SOlivier Houchard 47*1fb62fb0SOlivier Houchard struct ck_spinlock_anderson { 48*1fb62fb0SOlivier Houchard struct ck_spinlock_anderson_thread *slots; 49*1fb62fb0SOlivier Houchard unsigned int count; 50*1fb62fb0SOlivier Houchard unsigned int wrap; 51*1fb62fb0SOlivier Houchard unsigned int mask; 52*1fb62fb0SOlivier Houchard char pad[CK_MD_CACHELINE - sizeof(unsigned int) * 3 - sizeof(void *)]; 53*1fb62fb0SOlivier Houchard unsigned int next; 54*1fb62fb0SOlivier Houchard }; 55*1fb62fb0SOlivier Houchard typedef struct ck_spinlock_anderson ck_spinlock_anderson_t; 56*1fb62fb0SOlivier Houchard 57*1fb62fb0SOlivier Houchard CK_CC_INLINE static void 58*1fb62fb0SOlivier Houchard ck_spinlock_anderson_init(struct ck_spinlock_anderson *lock, 59*1fb62fb0SOlivier Houchard struct ck_spinlock_anderson_thread *slots, 60*1fb62fb0SOlivier Houchard unsigned int count) 61*1fb62fb0SOlivier Houchard { 62*1fb62fb0SOlivier Houchard unsigned int i; 63*1fb62fb0SOlivier Houchard 64*1fb62fb0SOlivier Houchard slots[0].locked = false; 65*1fb62fb0SOlivier Houchard slots[0].position = 0; 66*1fb62fb0SOlivier Houchard for (i = 1; i < count; i++) { 67*1fb62fb0SOlivier Houchard slots[i].locked = true; 68*1fb62fb0SOlivier Houchard slots[i].position = i; 69*1fb62fb0SOlivier Houchard } 70*1fb62fb0SOlivier Houchard 71*1fb62fb0SOlivier Houchard lock->slots = slots; 72*1fb62fb0SOlivier Houchard lock->count = count; 73*1fb62fb0SOlivier Houchard lock->mask = count - 1; 74*1fb62fb0SOlivier Houchard lock->next = 0; 75*1fb62fb0SOlivier Houchard 76*1fb62fb0SOlivier Houchard /* 77*1fb62fb0SOlivier Houchard * If the number of threads is not a power of two then compute 78*1fb62fb0SOlivier Houchard * appropriate wrap-around value in the case of next slot counter 79*1fb62fb0SOlivier Houchard * overflow. 80*1fb62fb0SOlivier Houchard */ 81*1fb62fb0SOlivier Houchard if (count & (count - 1)) 82*1fb62fb0SOlivier Houchard lock->wrap = (UINT_MAX % count) + 1; 83*1fb62fb0SOlivier Houchard else 84*1fb62fb0SOlivier Houchard lock->wrap = 0; 85*1fb62fb0SOlivier Houchard 86*1fb62fb0SOlivier Houchard ck_pr_barrier(); 87*1fb62fb0SOlivier Houchard return; 88*1fb62fb0SOlivier Houchard } 89*1fb62fb0SOlivier Houchard 90*1fb62fb0SOlivier Houchard CK_CC_INLINE static bool 91*1fb62fb0SOlivier Houchard ck_spinlock_anderson_locked(struct ck_spinlock_anderson *lock) 92*1fb62fb0SOlivier Houchard { 93*1fb62fb0SOlivier Houchard unsigned int position; 94*1fb62fb0SOlivier Houchard bool r; 95*1fb62fb0SOlivier Houchard 96*1fb62fb0SOlivier Houchard position = ck_pr_load_uint(&lock->next) & lock->mask; 97*1fb62fb0SOlivier Houchard r = ck_pr_load_uint(&lock->slots[position].locked); 98*1fb62fb0SOlivier Houchard ck_pr_fence_acquire(); 99*1fb62fb0SOlivier Houchard return r; 100*1fb62fb0SOlivier Houchard } 101*1fb62fb0SOlivier Houchard 102*1fb62fb0SOlivier Houchard CK_CC_INLINE static void 103*1fb62fb0SOlivier Houchard ck_spinlock_anderson_lock(struct ck_spinlock_anderson *lock, 104*1fb62fb0SOlivier Houchard struct ck_spinlock_anderson_thread **slot) 105*1fb62fb0SOlivier Houchard { 106*1fb62fb0SOlivier Houchard unsigned int position, next; 107*1fb62fb0SOlivier Houchard unsigned int count = lock->count; 108*1fb62fb0SOlivier Houchard 109*1fb62fb0SOlivier Houchard /* 110*1fb62fb0SOlivier Houchard * If count is not a power of 2, then it is possible for an overflow 111*1fb62fb0SOlivier Houchard * to reallocate beginning slots to more than one thread. To avoid this 112*1fb62fb0SOlivier Houchard * use a compare-and-swap. 113*1fb62fb0SOlivier Houchard */ 114*1fb62fb0SOlivier Houchard if (lock->wrap != 0) { 115*1fb62fb0SOlivier Houchard position = ck_pr_load_uint(&lock->next); 116*1fb62fb0SOlivier Houchard 117*1fb62fb0SOlivier Houchard do { 118*1fb62fb0SOlivier Houchard if (position == UINT_MAX) 119*1fb62fb0SOlivier Houchard next = lock->wrap; 120*1fb62fb0SOlivier Houchard else 121*1fb62fb0SOlivier Houchard next = position + 1; 122*1fb62fb0SOlivier Houchard } while (ck_pr_cas_uint_value(&lock->next, position, 123*1fb62fb0SOlivier Houchard next, &position) == false); 124*1fb62fb0SOlivier Houchard 125*1fb62fb0SOlivier Houchard position %= count; 126*1fb62fb0SOlivier Houchard } else { 127*1fb62fb0SOlivier Houchard position = ck_pr_faa_uint(&lock->next, 1); 128*1fb62fb0SOlivier Houchard position &= lock->mask; 129*1fb62fb0SOlivier Houchard } 130*1fb62fb0SOlivier Houchard 131*1fb62fb0SOlivier Houchard /* Serialize with respect to previous thread's store. */ 132*1fb62fb0SOlivier Houchard ck_pr_fence_load(); 133*1fb62fb0SOlivier Houchard 134*1fb62fb0SOlivier Houchard /* 135*1fb62fb0SOlivier Houchard * Spin until slot is marked as unlocked. First slot is initialized to 136*1fb62fb0SOlivier Houchard * false. 137*1fb62fb0SOlivier Houchard */ 138*1fb62fb0SOlivier Houchard while (ck_pr_load_uint(&lock->slots[position].locked) == true) 139*1fb62fb0SOlivier Houchard ck_pr_stall(); 140*1fb62fb0SOlivier Houchard 141*1fb62fb0SOlivier Houchard /* Prepare slot for potential re-use by another thread. */ 142*1fb62fb0SOlivier Houchard ck_pr_store_uint(&lock->slots[position].locked, true); 143*1fb62fb0SOlivier Houchard ck_pr_fence_lock(); 144*1fb62fb0SOlivier Houchard 145*1fb62fb0SOlivier Houchard *slot = lock->slots + position; 146*1fb62fb0SOlivier Houchard return; 147*1fb62fb0SOlivier Houchard } 148*1fb62fb0SOlivier Houchard 149*1fb62fb0SOlivier Houchard CK_CC_INLINE static void 150*1fb62fb0SOlivier Houchard ck_spinlock_anderson_unlock(struct ck_spinlock_anderson *lock, 151*1fb62fb0SOlivier Houchard struct ck_spinlock_anderson_thread *slot) 152*1fb62fb0SOlivier Houchard { 153*1fb62fb0SOlivier Houchard unsigned int position; 154*1fb62fb0SOlivier Houchard 155*1fb62fb0SOlivier Houchard ck_pr_fence_unlock(); 156*1fb62fb0SOlivier Houchard 157*1fb62fb0SOlivier Houchard /* Mark next slot as available. */ 158*1fb62fb0SOlivier Houchard if (lock->wrap == 0) 159*1fb62fb0SOlivier Houchard position = (slot->position + 1) & lock->mask; 160*1fb62fb0SOlivier Houchard else 161*1fb62fb0SOlivier Houchard position = (slot->position + 1) % lock->count; 162*1fb62fb0SOlivier Houchard 163*1fb62fb0SOlivier Houchard ck_pr_store_uint(&lock->slots[position].locked, false); 164*1fb62fb0SOlivier Houchard return; 165*1fb62fb0SOlivier Houchard } 166*1fb62fb0SOlivier Houchard #endif /* CK_F_SPINLOCK_ANDERSON */ 167*1fb62fb0SOlivier Houchard #endif /* CK_SPINLOCK_ANDERSON_H */ 168