guard.cc revision 278724
1232950Stheraven/* 2232950Stheraven * Copyright 2010-2012 PathScale, Inc. All rights reserved. 3232950Stheraven * 4232950Stheraven * Redistribution and use in source and binary forms, with or without 5232950Stheraven * modification, are permitted provided that the following conditions are met: 6232950Stheraven * 7232950Stheraven * 1. Redistributions of source code must retain the above copyright notice, 8232950Stheraven * this list of conditions and the following disclaimer. 9232950Stheraven * 10232950Stheraven * 2. Redistributions in binary form must reproduce the above copyright notice, 11232950Stheraven * this list of conditions and the following disclaimer in the documentation 12232950Stheraven * and/or other materials provided with the distribution. 13232950Stheraven * 14232950Stheraven * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS 15232950Stheraven * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16232950Stheraven * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17232950Stheraven * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 18232950Stheraven * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19232950Stheraven * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20232950Stheraven * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21232950Stheraven * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22232950Stheraven * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23232950Stheraven * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 24232950Stheraven * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25232950Stheraven */ 26232950Stheraven 27227825Stheraven/** 28227825Stheraven * guard.cc: Functions for thread-safe static initialisation. 29227825Stheraven * 30227825Stheraven * Static values in C++ can be initialised lazily their first use. This file 31227825Stheraven * contains functions that are used to ensure that two threads attempting to 32227825Stheraven * initialize the same static do not call the constructor twice. This is 33227825Stheraven * important because constructors can have side effects, so calling the 34227825Stheraven * constructor twice may be very bad. 35227825Stheraven * 36227825Stheraven * Statics that require initialisation are protected by a 64-bit value. Any 37227825Stheraven * platform that can do 32-bit atomic test and set operations can use this 38227825Stheraven * value as a low-overhead lock. Because statics (in most sane code) are 39227825Stheraven * accessed far more times than they are initialised, this lock implementation 40227825Stheraven * is heavily optimised towards the case where the static has already been 41227825Stheraven * initialised. 42227825Stheraven */ 43227825Stheraven#include <stdint.h> 44253159Stheraven#include <stdlib.h> 45253159Stheraven#include <stdio.h> 46227825Stheraven#include <pthread.h> 47227972Stheraven#include <assert.h> 48253159Stheraven#include "atomic.h" 49227825Stheraven 50253159Stheraven// Older GCC doesn't define __LITTLE_ENDIAN__ 51253159Stheraven#ifndef __LITTLE_ENDIAN__ 52253159Stheraven // If __BYTE_ORDER__ is defined, use that instead 53253159Stheraven# ifdef __BYTE_ORDER__ 54253159Stheraven# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 55253159Stheraven# define __LITTLE_ENDIAN__ 56253159Stheraven# endif 57253159Stheraven // x86 and ARM are the most common little-endian CPUs, so let's have a 58253159Stheraven // special case for them (ARM is already special cased). Assume everything 59253159Stheraven // else is big endian. 60253159Stheraven# elif defined(__x86_64) || defined(__i386) 61253159Stheraven# define __LITTLE_ENDIAN__ 62253159Stheraven# endif 63253159Stheraven#endif 64253159Stheraven 65253159Stheraven 66253159Stheraven/* 67253159Stheraven * The least significant bit of the guard variable indicates that the object 68253159Stheraven * has been initialised, the most significant bit is used for a spinlock. 69253159Stheraven */ 70227972Stheraven#ifdef __arm__ 71227972Stheraven// ARM ABI - 32-bit guards. 72253159Stheraventypedef uint32_t guard_t; 73278724Sdimtypedef uint32_t guard_lock_t; 74278724Sdimstatic const uint32_t LOCKED = static_cast<guard_t>(1) << 31; 75253159Stheravenstatic const uint32_t INITIALISED = 1; 76278724Sdim#define LOCK_PART(guard) (guard) 77278724Sdim#define INIT_PART(guard) (guard) 78278724Sdim#elif defined(_LP64) 79253159Stheraventypedef uint64_t guard_t; 80278724Sdimtypedef uint64_t guard_lock_t; 81253159Stheraven# if defined(__LITTLE_ENDIAN__) 82278724Sdimstatic const guard_t LOCKED = static_cast<guard_t>(1) << 63; 83253159Stheravenstatic const guard_t INITIALISED = 1; 84253159Stheraven# else 85253159Stheravenstatic const guard_t LOCKED = 1; 86278724Sdimstatic const guard_t INITIALISED = static_cast<guard_t>(1) << 56; 87253159Stheraven# endif 88278724Sdim#define LOCK_PART(guard) (guard) 89278724Sdim#define INIT_PART(guard) (guard) 90278724Sdim#else 91278724Sdimtypedef uint32_t guard_lock_t; 92278724Sdim# if defined(__LITTLE_ENDIAN__) 93278724Sdimtypedef struct { 94278724Sdim uint32_t init_half; 95278724Sdim uint32_t lock_half; 96278724Sdim} guard_t; 97278724Sdimstatic const uint32_t LOCKED = static_cast<guard_lock_t>(1) << 31; 98278724Sdimstatic const uint32_t INITIALISED = 1; 99278724Sdim# else 100278724Sdimtypedef struct { 101278724Sdim uint32_t init_half; 102278724Sdim uint32_t lock_half; 103278724Sdim} guard_t; 104278724Sdim_Static_assert(sizeof(guard_t) == sizeof(uint64_t), ""); 105278724Sdimstatic const uint32_t LOCKED = 1; 106278724Sdimstatic const uint32_t INITIALISED = static_cast<guard_lock_t>(1) << 24; 107278724Sdim# endif 108278724Sdim#define LOCK_PART(guard) (&(guard)->lock_half) 109278724Sdim#define INIT_PART(guard) (&(guard)->init_half) 110253159Stheraven#endif 111278724Sdimstatic const guard_lock_t INITIAL = 0; 112227972Stheraven 113227825Stheraven/** 114227972Stheraven * Acquires a lock on a guard, returning 0 if the object has already been 115227972Stheraven * initialised, and 1 if it has not. If the object is already constructed then 116227972Stheraven * this function just needs to read a byte from memory and return. 117227972Stheraven */ 118253159Stheravenextern "C" int __cxa_guard_acquire(volatile guard_t *guard_object) 119227972Stheraven{ 120278724Sdim guard_lock_t old; 121253159Stheraven // Not an atomic read, doesn't establish a happens-before relationship, but 122253159Stheraven // if one is already established and we end up seeing an initialised state 123253159Stheraven // then it's a fast path, otherwise we'll do something more expensive than 124253159Stheraven // this test anyway... 125278724Sdim if (INITIALISED == *INIT_PART(guard_object)) 126278724Sdim return 0; 127253159Stheraven // Spin trying to do the initialisation 128278724Sdim for (;;) 129227972Stheraven { 130253159Stheraven // Loop trying to move the value of the guard from 0 (not 131253159Stheraven // locked, not initialised) to the locked-uninitialised 132253159Stheraven // position. 133278724Sdim old = __sync_val_compare_and_swap(LOCK_PART(guard_object), 134278724Sdim INITIAL, LOCKED); 135278724Sdim if (old == INITIAL) { 136278724Sdim // Lock obtained. If lock and init bit are 137278724Sdim // in separate words, check for init race. 138278724Sdim if (INIT_PART(guard_object) == LOCK_PART(guard_object)) 139253159Stheraven return 1; 140278724Sdim if (INITIALISED != *INIT_PART(guard_object)) 141278724Sdim return 1; 142278724Sdim 143278724Sdim // No need for a memory barrier here, 144278724Sdim // see first comment. 145278724Sdim *LOCK_PART(guard_object) = INITIAL; 146278724Sdim return 0; 147227972Stheraven } 148278724Sdim // If lock and init bit are in the same word, check again 149278724Sdim // if we are done. 150278724Sdim if (INIT_PART(guard_object) == LOCK_PART(guard_object) && 151278724Sdim old == INITIALISED) 152278724Sdim return 0; 153278724Sdim 154278724Sdim assert(old == LOCKED); 155278724Sdim // Another thread holds the lock. 156278724Sdim // If lock and init bit are in different words, check 157278724Sdim // if we are done before yielding and looping. 158278724Sdim if (INIT_PART(guard_object) != LOCK_PART(guard_object) && 159278724Sdim INITIALISED == *INIT_PART(guard_object)) 160278724Sdim return 0; 161278724Sdim sched_yield(); 162227972Stheraven } 163227972Stheraven} 164227972Stheraven 165227972Stheraven/** 166227972Stheraven * Releases the lock without marking the object as initialised. This function 167227972Stheraven * is called if initialising a static causes an exception to be thrown. 168227972Stheraven */ 169253159Stheravenextern "C" void __cxa_guard_abort(volatile guard_t *guard_object) 170227972Stheraven{ 171253159Stheraven __attribute__((unused)) 172278724Sdim bool reset = __sync_bool_compare_and_swap(LOCK_PART(guard_object), 173278724Sdim LOCKED, INITIAL); 174253159Stheraven assert(reset); 175227972Stheraven} 176227972Stheraven/** 177227972Stheraven * Releases the guard and marks the object as initialised. This function is 178227972Stheraven * called after successful initialisation of a static. 179227972Stheraven */ 180253159Stheravenextern "C" void __cxa_guard_release(volatile guard_t *guard_object) 181227972Stheraven{ 182278724Sdim guard_lock_t old; 183278724Sdim if (INIT_PART(guard_object) == LOCK_PART(guard_object)) 184278724Sdim old = LOCKED; 185278724Sdim else 186278724Sdim old = INITIAL; 187253159Stheraven __attribute__((unused)) 188278724Sdim bool reset = __sync_bool_compare_and_swap(INIT_PART(guard_object), 189278724Sdim old, INITIALISED); 190253159Stheraven assert(reset); 191278724Sdim if (INIT_PART(guard_object) != LOCK_PART(guard_object)) 192278724Sdim *LOCK_PART(guard_object) = INITIAL; 193227972Stheraven} 194