guard.cc revision 227972
1227825Stheraven/**
2227825Stheraven * guard.cc: Functions for thread-safe static initialisation.
3227825Stheraven *
4227825Stheraven * Static values in C++ can be initialised lazily their first use.  This file
5227825Stheraven * contains functions that are used to ensure that two threads attempting to
6227825Stheraven * initialize the same static do not call the constructor twice.  This is
7227825Stheraven * important because constructors can have side effects, so calling the
8227825Stheraven * constructor twice may be very bad.
9227825Stheraven *
10227825Stheraven * Statics that require initialisation are protected by a 64-bit value.  Any
11227825Stheraven * platform that can do 32-bit atomic test and set operations can use this
12227825Stheraven * value as a low-overhead lock.  Because statics (in most sane code) are
13227825Stheraven * accessed far more times than they are initialised, this lock implementation
14227825Stheraven * is heavily optimised towards the case where the static has already been
15227825Stheraven * initialised.
16227825Stheraven */
17227825Stheraven#include <stdint.h>
18227825Stheraven#include <pthread.h>
19227972Stheraven#include <assert.h>
20227825Stheraven
21227972Stheraven#ifdef __arm__
22227972Stheraven// ARM ABI - 32-bit guards.
23227972Stheraven
24227825Stheraven/**
25227972Stheraven * Acquires a lock on a guard, returning 0 if the object has already been
26227972Stheraven * initialised, and 1 if it has not.  If the object is already constructed then
27227972Stheraven * this function just needs to read a byte from memory and return.
28227972Stheraven */
29227972Stheravenextern "C" int __cxa_guard_acquire(volatile int32_t *guard_object)
30227972Stheraven{
31227972Stheraven	if ((1<<31) == *guard_object) { return 0; }
32227972Stheraven	// If we can atomically move the value from 0 -> 1, then this is
33227972Stheraven	// uninitialised.
34227972Stheraven	if (__sync_bool_compare_and_swap(guard_object, 0, 1))
35227972Stheraven	{
36227972Stheraven		return 1;
37227972Stheraven	}
38227972Stheraven	// If the value is not 0, some other thread was initialising this.  Spin
39227972Stheraven	// until it's finished.
40227972Stheraven	while (__sync_bool_compare_and_swap(guard_object, (1<<31), (1<<31)))
41227972Stheraven	{
42227972Stheraven		// If the other thread aborted, then we grab the lock
43227972Stheraven		if (__sync_bool_compare_and_swap(guard_object, 0, 1))
44227972Stheraven		{
45227972Stheraven			return 1;
46227972Stheraven		}
47227972Stheraven		sched_yield();
48227972Stheraven	}
49227972Stheraven	return 0;
50227972Stheraven}
51227972Stheraven
52227972Stheraven/**
53227972Stheraven * Releases the lock without marking the object as initialised.  This function
54227972Stheraven * is called if initialising a static causes an exception to be thrown.
55227972Stheraven */
56227972Stheravenextern "C" void __cxa_guard_abort(int32_t *guard_object)
57227972Stheraven{
58227972Stheraven	assert(__sync_bool_compare_and_swap(guard_object, 1, 0));
59227972Stheraven}
60227972Stheraven/**
61227972Stheraven * Releases the guard and marks the object as initialised.  This function is
62227972Stheraven * called after successful initialisation of a static.
63227972Stheraven */
64227972Stheravenextern "C" void __cxa_guard_release(int32_t *guard_object)
65227972Stheraven{
66227972Stheraven	assert(__sync_bool_compare_and_swap(guard_object, 1, (1<<31)));
67227972Stheraven}
68227972Stheraven
69227972Stheraven
70227972Stheraven#else
71227972Stheraven// Itanium ABI: 64-bit guards
72227972Stheraven
73227972Stheraven/**
74227825Stheraven * Returns a pointer to the low 32 bits in a 64-bit value, respecting the
75227825Stheraven * platform's byte order.
76227825Stheraven */
77227825Stheravenstatic int32_t *low_32_bits(volatile int64_t *ptr)
78227825Stheraven{
79227825Stheraven	int32_t *low= (int32_t*)ptr;
80227825Stheraven	// Test if the machine is big endian - constant propagation at compile time
81227825Stheraven	// should eliminate this completely.
82227825Stheraven	int one = 1;
83227825Stheraven	if (*(char*)&one != 1)
84227825Stheraven	{
85227825Stheraven		low++;
86227825Stheraven	}
87227825Stheraven	return low;
88227825Stheraven}
89227825Stheraven
90227825Stheraven/**
91227825Stheraven * Acquires a lock on a guard, returning 0 if the object has already been
92227825Stheraven * initialised, and 1 if it has not.  If the object is already constructed then
93227825Stheraven * this function just needs to read a byte from memory and return.
94227825Stheraven */
95227825Stheravenextern "C" int __cxa_guard_acquire(volatile int64_t *guard_object)
96227825Stheraven{
97227825Stheraven	char first_byte = (*guard_object) >> 56;
98227825Stheraven	if (1 == first_byte) { return 0; }
99227825Stheraven	int32_t *lock = low_32_bits(guard_object);
100227825Stheraven	// Simple spin lock using the low 32 bits.  We assume that concurrent
101227825Stheraven	// attempts to initialize statics are very rare, so we don't need to
102227825Stheraven	// optimise for the case where we have lots of threads trying to acquire
103227825Stheraven	// the lock at the same time.
104227825Stheraven	while (!__sync_bool_compare_and_swap_4(lock, 0, 1))
105227825Stheraven	{
106227825Stheraven		sched_yield();
107227825Stheraven	}
108227825Stheraven	// We have to test the guard again, in case another thread has performed
109227825Stheraven	// the initialisation while we were trying to acquire the lock.
110227825Stheraven	first_byte = (*guard_object) >> 56;
111227825Stheraven	return (1 != first_byte);
112227825Stheraven}
113227825Stheraven
114227825Stheraven/**
115227825Stheraven * Releases the lock without marking the object as initialised.  This function
116227825Stheraven * is called if initialising a static causes an exception to be thrown.
117227825Stheraven */
118227825Stheravenextern "C" void __cxa_guard_abort(int64_t *guard_object)
119227825Stheraven{
120227825Stheraven	int32_t *lock = low_32_bits(guard_object);
121227825Stheraven	*lock = 0;
122227825Stheraven}
123227825Stheraven/**
124227825Stheraven * Releases the guard and marks the object as initialised.  This function is
125227825Stheraven * called after successful initialisation of a static.
126227825Stheraven */
127227825Stheravenextern "C" void __cxa_guard_release(int64_t *guard_object)
128227825Stheraven{
129227825Stheraven	// Set the first byte to 1
130227825Stheraven	*guard_object |= ((int64_t)1) << 56;
131227825Stheraven	__cxa_guard_abort(guard_object);
132227825Stheraven}
133227825Stheraven
134227972Stheraven#endif
135