194e3ee44SDavid Chisnall /*
294e3ee44SDavid Chisnall * Copyright 2010-2012 PathScale, Inc. All rights reserved.
3bfffb66eSDimitry Andric * Copyright 2021 David Chisnall. All rights reserved.
494e3ee44SDavid Chisnall *
594e3ee44SDavid Chisnall * Redistribution and use in source and binary forms, with or without
694e3ee44SDavid Chisnall * modification, are permitted provided that the following conditions are met:
794e3ee44SDavid Chisnall *
894e3ee44SDavid Chisnall * 1. Redistributions of source code must retain the above copyright notice,
994e3ee44SDavid Chisnall * this list of conditions and the following disclaimer.
1094e3ee44SDavid Chisnall *
1194e3ee44SDavid Chisnall * 2. Redistributions in binary form must reproduce the above copyright notice,
1294e3ee44SDavid Chisnall * this list of conditions and the following disclaimer in the documentation
1394e3ee44SDavid Chisnall * and/or other materials provided with the distribution.
1494e3ee44SDavid Chisnall *
1594e3ee44SDavid Chisnall * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
1694e3ee44SDavid Chisnall * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
1794e3ee44SDavid Chisnall * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1894e3ee44SDavid Chisnall * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
1994e3ee44SDavid Chisnall * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
2094e3ee44SDavid Chisnall * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2194e3ee44SDavid Chisnall * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2294e3ee44SDavid Chisnall * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2394e3ee44SDavid Chisnall * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
2494e3ee44SDavid Chisnall * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
2594e3ee44SDavid Chisnall * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2694e3ee44SDavid Chisnall */
2794e3ee44SDavid Chisnall
287a984708SDavid Chisnall /**
297a984708SDavid Chisnall * guard.cc: Functions for thread-safe static initialisation.
307a984708SDavid Chisnall *
317a984708SDavid Chisnall * Static values in C++ can be initialised lazily their first use. This file
327a984708SDavid Chisnall * contains functions that are used to ensure that two threads attempting to
337a984708SDavid Chisnall * initialize the same static do not call the constructor twice. This is
347a984708SDavid Chisnall * important because constructors can have side effects, so calling the
357a984708SDavid Chisnall * constructor twice may be very bad.
367a984708SDavid Chisnall *
377a984708SDavid Chisnall * Statics that require initialisation are protected by a 64-bit value. Any
387a984708SDavid Chisnall * platform that can do 32-bit atomic test and set operations can use this
397a984708SDavid Chisnall * value as a low-overhead lock. Because statics (in most sane code) are
407a984708SDavid Chisnall * accessed far more times than they are initialised, this lock implementation
417a984708SDavid Chisnall * is heavily optimised towards the case where the static has already been
427a984708SDavid Chisnall * initialised.
437a984708SDavid Chisnall */
44bfffb66eSDimitry Andric #include "atomic.h"
45bfffb66eSDimitry Andric #include <assert.h>
46bfffb66eSDimitry Andric #include <pthread.h>
477a984708SDavid Chisnall #include <stdint.h>
484bab9fd9SDavid Chisnall #include <stdlib.h>
497a984708SDavid Chisnall
504bab9fd9SDavid Chisnall // Older GCC doesn't define __LITTLE_ENDIAN__
514bab9fd9SDavid Chisnall #ifndef __LITTLE_ENDIAN__
524bab9fd9SDavid Chisnall // If __BYTE_ORDER__ is defined, use that instead
534bab9fd9SDavid Chisnall # ifdef __BYTE_ORDER__
544bab9fd9SDavid Chisnall # if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
554bab9fd9SDavid Chisnall # define __LITTLE_ENDIAN__
564bab9fd9SDavid Chisnall # endif
574bab9fd9SDavid Chisnall // x86 and ARM are the most common little-endian CPUs, so let's have a
584bab9fd9SDavid Chisnall // special case for them (ARM is already special cased). Assume everything
594bab9fd9SDavid Chisnall // else is big endian.
604bab9fd9SDavid Chisnall # elif defined(__x86_64) || defined(__i386)
614bab9fd9SDavid Chisnall # define __LITTLE_ENDIAN__
624bab9fd9SDavid Chisnall # endif
634bab9fd9SDavid Chisnall #endif
644bab9fd9SDavid Chisnall
654bab9fd9SDavid Chisnall /*
66bfffb66eSDimitry Andric * The Itanium C++ ABI defines guard words that are 64-bit (32-bit on AArch32)
67bfffb66eSDimitry Andric * values with one bit defined to indicate that the guarded variable is and
68bfffb66eSDimitry Andric * another bit to indicate that it's currently locked (initialisation in
69bfffb66eSDimitry Andric * progress). The bit to use depends on the byte order of the target.
70bfffb66eSDimitry Andric *
71bfffb66eSDimitry Andric * On many 32-bit platforms, 64-bit atomics are unavailable (or slow) and so we
72*7819a911SDimitry Andric * treat the two halves of the 64-bit word as independent values and establish
73*7819a911SDimitry Andric * an ordering on them such that the guard word is never modified unless the
74*7819a911SDimitry Andric * lock word is in the locked state. This means that we can do double-checked
75*7819a911SDimitry Andric * locking by loading the guard word and, if it is not initialised, trying to
76*7819a911SDimitry Andric * transition the lock word from the unlocked to locked state, and then
77*7819a911SDimitry Andric * manipulate the guard word.
784bab9fd9SDavid Chisnall */
79bfffb66eSDimitry Andric namespace
80bfffb66eSDimitry Andric {
81bfffb66eSDimitry Andric /**
82bfffb66eSDimitry Andric * The state of the guard variable when an attempt is made to lock it.
83bfffb66eSDimitry Andric */
84bfffb66eSDimitry Andric enum class GuardState
85bfffb66eSDimitry Andric {
86bfffb66eSDimitry Andric /**
87bfffb66eSDimitry Andric * The lock is not held but is not needed because initialisation is
88bfffb66eSDimitry Andric * one.
89bfffb66eSDimitry Andric */
90bfffb66eSDimitry Andric InitDone,
91bfffb66eSDimitry Andric
92bfffb66eSDimitry Andric /**
93bfffb66eSDimitry Andric * Initialisation is not done but the lock is held by the caller.
94bfffb66eSDimitry Andric */
95bfffb66eSDimitry Andric InitLockSucceeded,
96bfffb66eSDimitry Andric
97bfffb66eSDimitry Andric /**
98bfffb66eSDimitry Andric * Attempting to acquire the lock failed.
99bfffb66eSDimitry Andric */
100bfffb66eSDimitry Andric InitLockFailed
101bfffb66eSDimitry Andric };
102bfffb66eSDimitry Andric
103bfffb66eSDimitry Andric /**
104bfffb66eSDimitry Andric * Class encapsulating a single atomic word being used to represent the
105bfffb66eSDimitry Andric * guard. The word size is defined by the type of `GuardWord`. The bit
106bfffb66eSDimitry Andric * used to indicate the locked state is `1<<LockedBit`, the bit used to
107bfffb66eSDimitry Andric * indicate the initialised state is `1<<InitBit`.
108bfffb66eSDimitry Andric */
109bfffb66eSDimitry Andric template<typename GuardWord, int LockedBit, int InitBit>
110bfffb66eSDimitry Andric struct SingleWordGuard
111bfffb66eSDimitry Andric {
112bfffb66eSDimitry Andric /**
113bfffb66eSDimitry Andric * The value indicating that the lock bit is set (and no other bits).
114bfffb66eSDimitry Andric */
115bfffb66eSDimitry Andric static constexpr GuardWord locked = static_cast<GuardWord>(1)
116bfffb66eSDimitry Andric << LockedBit;
117bfffb66eSDimitry Andric
118bfffb66eSDimitry Andric /**
119bfffb66eSDimitry Andric * The value indicating that the initialised bit is set (and all other
120bfffb66eSDimitry Andric * bits are zero).
121bfffb66eSDimitry Andric */
122bfffb66eSDimitry Andric static constexpr GuardWord initialised = static_cast<GuardWord>(1)
123bfffb66eSDimitry Andric << InitBit;
124bfffb66eSDimitry Andric
125bfffb66eSDimitry Andric /**
126bfffb66eSDimitry Andric * The guard variable.
127bfffb66eSDimitry Andric */
128bfffb66eSDimitry Andric atomic<GuardWord> val;
129bfffb66eSDimitry Andric
130bfffb66eSDimitry Andric public:
131bfffb66eSDimitry Andric /**
132bfffb66eSDimitry Andric * Release the lock and set the initialised state. In the single-word
133bfffb66eSDimitry Andric * implementation here, these are both done by a single store.
134bfffb66eSDimitry Andric */
unlock__anone6a749c70111::SingleWordGuard135bfffb66eSDimitry Andric void unlock(bool isInitialised)
136bfffb66eSDimitry Andric {
137bfffb66eSDimitry Andric val.store(isInitialised ? initialised : 0, memory_order::release);
138bfffb66eSDimitry Andric #ifndef NDEBUG
139bfffb66eSDimitry Andric GuardWord init_state = initialised;
140bfffb66eSDimitry Andric assert(*reinterpret_cast<uint8_t*>(&init_state) != 0);
141bfffb66eSDimitry Andric #endif
142bfffb66eSDimitry Andric }
143bfffb66eSDimitry Andric
144bfffb66eSDimitry Andric /**
145bfffb66eSDimitry Andric * Try to acquire the lock. This has a tri-state return, indicating
146bfffb66eSDimitry Andric * either that the lock was acquired, it wasn't acquired because it was
147bfffb66eSDimitry Andric * contended, or it wasn't acquired because the guarded variable is
148bfffb66eSDimitry Andric * already initialised.
149bfffb66eSDimitry Andric */
try_lock__anone6a749c70111::SingleWordGuard150bfffb66eSDimitry Andric GuardState try_lock()
151bfffb66eSDimitry Andric {
152bfffb66eSDimitry Andric GuardWord old = 0;
153bfffb66eSDimitry Andric // Try to acquire the lock, assuming that we are in the state where
154bfffb66eSDimitry Andric // the lock is not held and the variable is not initialised (so the
155bfffb66eSDimitry Andric // expected value is 0).
156bfffb66eSDimitry Andric if (val.compare_exchange(old, locked))
157bfffb66eSDimitry Andric {
158bfffb66eSDimitry Andric return GuardState::InitLockSucceeded;
159bfffb66eSDimitry Andric }
160bfffb66eSDimitry Andric // If the CAS failed and the old value indicates that this is
161bfffb66eSDimitry Andric // initialised, return that initialisation is done and skip further
162bfffb66eSDimitry Andric // retries.
163bfffb66eSDimitry Andric if (old == initialised)
164bfffb66eSDimitry Andric {
165bfffb66eSDimitry Andric return GuardState::InitDone;
166bfffb66eSDimitry Andric }
167bfffb66eSDimitry Andric // Otherwise, report failure.
168bfffb66eSDimitry Andric return GuardState::InitLockFailed;
169bfffb66eSDimitry Andric }
170bfffb66eSDimitry Andric
171bfffb66eSDimitry Andric /**
172bfffb66eSDimitry Andric * Check whether the guard indicates that the variable is initialised.
173bfffb66eSDimitry Andric */
is_initialised__anone6a749c70111::SingleWordGuard174bfffb66eSDimitry Andric bool is_initialised()
175bfffb66eSDimitry Andric {
176bfffb66eSDimitry Andric return (val.load(memory_order::acquire) & initialised) ==
177bfffb66eSDimitry Andric initialised;
178bfffb66eSDimitry Andric }
179bfffb66eSDimitry Andric };
180bfffb66eSDimitry Andric
181bfffb66eSDimitry Andric /**
182bfffb66eSDimitry Andric * Class encapsulating using two 32-bit atomic values to represent a 64-bit
183bfffb66eSDimitry Andric * guard variable.
184bfffb66eSDimitry Andric */
185bfffb66eSDimitry Andric template<int LockedBit, int InitBit>
186bfffb66eSDimitry Andric class DoubleWordGuard
187bfffb66eSDimitry Andric {
188bfffb66eSDimitry Andric /**
189bfffb66eSDimitry Andric * The value of `lock_word` when the lock is held.
190bfffb66eSDimitry Andric */
191bfffb66eSDimitry Andric static constexpr uint32_t locked = static_cast<uint32_t>(1)
192bfffb66eSDimitry Andric << LockedBit;
193bfffb66eSDimitry Andric
194bfffb66eSDimitry Andric /**
195bfffb66eSDimitry Andric * The value of `init_word` when the guarded variable is initialised.
196bfffb66eSDimitry Andric */
197bfffb66eSDimitry Andric static constexpr uint32_t initialised = static_cast<uint32_t>(1)
198bfffb66eSDimitry Andric << InitBit;
199bfffb66eSDimitry Andric
200bfffb66eSDimitry Andric /**
201bfffb66eSDimitry Andric * The word used for the initialised flag. This is always the first
202bfffb66eSDimitry Andric * word irrespective of endian because the generated code compares the
203bfffb66eSDimitry Andric * first byte in memory against 0.
204bfffb66eSDimitry Andric */
205bfffb66eSDimitry Andric atomic<uint32_t> init_word;
206bfffb66eSDimitry Andric
207bfffb66eSDimitry Andric /**
208bfffb66eSDimitry Andric * The word used for the lock.
209bfffb66eSDimitry Andric */
210bfffb66eSDimitry Andric atomic<uint32_t> lock_word;
211bfffb66eSDimitry Andric
212bfffb66eSDimitry Andric public:
213bfffb66eSDimitry Andric /**
214bfffb66eSDimitry Andric * Try to acquire the lock. This has a tri-state return, indicating
215bfffb66eSDimitry Andric * either that the lock was acquired, it wasn't acquired because it was
216bfffb66eSDimitry Andric * contended, or it wasn't acquired because the guarded variable is
217bfffb66eSDimitry Andric * already initialised.
218bfffb66eSDimitry Andric */
try_lock()219bfffb66eSDimitry Andric GuardState try_lock()
220bfffb66eSDimitry Andric {
221bfffb66eSDimitry Andric uint32_t old = 0;
222bfffb66eSDimitry Andric // Try to acquire the lock
223bfffb66eSDimitry Andric if (lock_word.compare_exchange(old, locked))
224bfffb66eSDimitry Andric {
225bfffb66eSDimitry Andric // If we succeeded, check if initialisation has happened. In
226bfffb66eSDimitry Andric // this version, we don't have atomic manipulation of both the
227bfffb66eSDimitry Andric // lock and initialised bits together. Instead, we have an
228bfffb66eSDimitry Andric // ordering rule that the initialised bit is only ever updated
229bfffb66eSDimitry Andric // with the lock held.
230bfffb66eSDimitry Andric if (is_initialised())
231bfffb66eSDimitry Andric {
232bfffb66eSDimitry Andric // If another thread did manage to initialise this, release
233bfffb66eSDimitry Andric // the lock and notify the caller that initialisation is
234bfffb66eSDimitry Andric // done.
235*7819a911SDimitry Andric lock_word.store(0, memory_order::release);
236bfffb66eSDimitry Andric return GuardState::InitDone;
237bfffb66eSDimitry Andric }
238bfffb66eSDimitry Andric return GuardState::InitLockSucceeded;
239bfffb66eSDimitry Andric }
240bfffb66eSDimitry Andric return GuardState::InitLockFailed;
241bfffb66eSDimitry Andric }
242bfffb66eSDimitry Andric
243bfffb66eSDimitry Andric /**
244bfffb66eSDimitry Andric * Set the initialised state and release the lock. In this
245bfffb66eSDimitry Andric * implementation, this is ordered, not atomic: the initialise bit is
246bfffb66eSDimitry Andric * set while the lock is held.
247bfffb66eSDimitry Andric */
unlock(bool isInitialised)248bfffb66eSDimitry Andric void unlock(bool isInitialised)
249bfffb66eSDimitry Andric {
250bfffb66eSDimitry Andric init_word.store(isInitialised ? initialised : 0,
251bfffb66eSDimitry Andric memory_order::release);
252bfffb66eSDimitry Andric lock_word.store(0, memory_order::release);
253bfffb66eSDimitry Andric assert((*reinterpret_cast<uint8_t*>(this) != 0) == isInitialised);
254bfffb66eSDimitry Andric }
255bfffb66eSDimitry Andric
256bfffb66eSDimitry Andric /**
257bfffb66eSDimitry Andric * Return whether the guarded variable is initialised.
258bfffb66eSDimitry Andric */
is_initialised()259bfffb66eSDimitry Andric bool is_initialised()
260bfffb66eSDimitry Andric {
261bfffb66eSDimitry Andric return (init_word.load(memory_order::acquire) & initialised) ==
262bfffb66eSDimitry Andric initialised;
263bfffb66eSDimitry Andric }
264bfffb66eSDimitry Andric };
265bfffb66eSDimitry Andric
266bfffb66eSDimitry Andric // Check that the two implementations are the correct size.
267bfffb66eSDimitry Andric static_assert(sizeof(SingleWordGuard<uint32_t, 31, 0>) == sizeof(uint32_t),
268bfffb66eSDimitry Andric "Single-word 32-bit guard must be 32 bits");
269bfffb66eSDimitry Andric static_assert(sizeof(SingleWordGuard<uint64_t, 63, 0>) == sizeof(uint64_t),
270bfffb66eSDimitry Andric "Single-word 64-bit guard must be 64 bits");
271bfffb66eSDimitry Andric static_assert(sizeof(DoubleWordGuard<31, 0>) == sizeof(uint64_t),
272bfffb66eSDimitry Andric "Double-word guard must be 64 bits");
273bfffb66eSDimitry Andric
2747a984708SDavid Chisnall #ifdef __arm__
275bfffb66eSDimitry Andric /**
276bfffb66eSDimitry Andric * The Arm PCS defines a variant of the Itanium ABI with 32-bit lock words.
277bfffb66eSDimitry Andric */
278bfffb66eSDimitry Andric using Guard = SingleWordGuard<uint32_t, 31, 0>;
279f2dc4184SDimitry Andric #elif defined(_LP64)
2804bab9fd9SDavid Chisnall # if defined(__LITTLE_ENDIAN__)
281bfffb66eSDimitry Andric /**
282bfffb66eSDimitry Andric * On little-endian 64-bit platforms the guard word is a single 64-bit
283bfffb66eSDimitry Andric * atomic with the lock in the high bit and the initialised flag in the low
284bfffb66eSDimitry Andric * bit.
285bfffb66eSDimitry Andric */
286bfffb66eSDimitry Andric using Guard = SingleWordGuard<uint64_t, 63, 0>;
2874bab9fd9SDavid Chisnall # else
288bfffb66eSDimitry Andric /**
289bfffb66eSDimitry Andric * On bit-endian 64-bit platforms, the guard word is a single 64-bit atomic
290bfffb66eSDimitry Andric * with the lock in the low bit and the initialised bit in the highest
291bfffb66eSDimitry Andric * byte.
292bfffb66eSDimitry Andric */
293bfffb66eSDimitry Andric using Guard = SingleWordGuard<uint64_t, 0, 56>;
2944bab9fd9SDavid Chisnall # endif
295f2dc4184SDimitry Andric #else
296f2dc4184SDimitry Andric # if defined(__LITTLE_ENDIAN__)
297bfffb66eSDimitry Andric /**
298bfffb66eSDimitry Andric * 32-bit platforms use the same layout as 64-bit.
299bfffb66eSDimitry Andric */
300bfffb66eSDimitry Andric using Guard = DoubleWordGuard<31, 0>;
301f2dc4184SDimitry Andric # else
302bfffb66eSDimitry Andric /**
303bfffb66eSDimitry Andric * 32-bit platforms use the same layout as 64-bit.
304bfffb66eSDimitry Andric */
305bfffb66eSDimitry Andric using Guard = DoubleWordGuard<0, 24>;
3064bab9fd9SDavid Chisnall # endif
307f2dc4184SDimitry Andric #endif
308bfffb66eSDimitry Andric
309bfffb66eSDimitry Andric } // namespace
3107a984708SDavid Chisnall
3117a984708SDavid Chisnall /**
3127a984708SDavid Chisnall * Acquires a lock on a guard, returning 0 if the object has already been
3137a984708SDavid Chisnall * initialised, and 1 if it has not. If the object is already constructed then
3147a984708SDavid Chisnall * this function just needs to read a byte from memory and return.
3157a984708SDavid Chisnall */
__cxa_guard_acquire(Guard * guard_object)316bfffb66eSDimitry Andric extern "C" int __cxa_guard_acquire(Guard *guard_object)
3177a984708SDavid Chisnall {
318bfffb66eSDimitry Andric // Check if this is already initialised. If so, we don't have to do
319bfffb66eSDimitry Andric // anything.
320bfffb66eSDimitry Andric if (guard_object->is_initialised())
32156aaed38SDimitry Andric {
32225482379SDimitry Andric return 0;
32356aaed38SDimitry Andric }
324bfffb66eSDimitry Andric // Spin trying to acquire the lock. If we fail to acquire the lock the
325bfffb66eSDimitry Andric // first time then another thread will *probably* initialise it, but if the
326bfffb66eSDimitry Andric // constructor throws an exception then we may have to try again in this
327bfffb66eSDimitry Andric // thread.
328bfffb66eSDimitry Andric for (;;)
329bfffb66eSDimitry Andric {
330bfffb66eSDimitry Andric // Try to acquire the lock.
331bfffb66eSDimitry Andric switch (guard_object->try_lock())
332bfffb66eSDimitry Andric {
333bfffb66eSDimitry Andric // If we failed to acquire the lock but another thread has
334bfffb66eSDimitry Andric // initialised the lock while we were waiting, return immediately
335bfffb66eSDimitry Andric // indicating that initialisation is not required.
336bfffb66eSDimitry Andric case GuardState::InitDone:
33725482379SDimitry Andric return 0;
338bfffb66eSDimitry Andric // If we acquired the lock, return immediately to start
339bfffb66eSDimitry Andric // initialisation.
340bfffb66eSDimitry Andric case GuardState::InitLockSucceeded:
341bfffb66eSDimitry Andric return 1;
342bfffb66eSDimitry Andric // If we didn't acquire the lock, pause and retry.
343bfffb66eSDimitry Andric case GuardState::InitLockFailed:
344bfffb66eSDimitry Andric break;
345bfffb66eSDimitry Andric }
3467a984708SDavid Chisnall sched_yield();
3477a984708SDavid Chisnall }
3484bab9fd9SDavid Chisnall }
3497a984708SDavid Chisnall
3507a984708SDavid Chisnall /**
3517a984708SDavid Chisnall * Releases the lock without marking the object as initialised. This function
3527a984708SDavid Chisnall * is called if initialising a static causes an exception to be thrown.
3537a984708SDavid Chisnall */
__cxa_guard_abort(Guard * guard_object)354bfffb66eSDimitry Andric extern "C" void __cxa_guard_abort(Guard *guard_object)
3557a984708SDavid Chisnall {
356bfffb66eSDimitry Andric guard_object->unlock(false);
3577a984708SDavid Chisnall }
358bfffb66eSDimitry Andric
3597a984708SDavid Chisnall /**
3607a984708SDavid Chisnall * Releases the guard and marks the object as initialised. This function is
3617a984708SDavid Chisnall * called after successful initialisation of a static.
3627a984708SDavid Chisnall */
__cxa_guard_release(Guard * guard_object)363bfffb66eSDimitry Andric extern "C" void __cxa_guard_release(Guard *guard_object)
3647a984708SDavid Chisnall {
365bfffb66eSDimitry Andric guard_object->unlock(true);
3667a984708SDavid Chisnall }
367