• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * one_time_construction.cpp
3  *
4  * Copyright 2006 The Android Open Source Project
5  *
6  * This file contains C++ ABI support functions for one time
7  * constructors as defined in the "Run-time ABI for the ARM Architecture"
8  * section 4.4.2
9  */
10 
11 #include <stddef.h>
12 #include <sys/atomics.h>
13 #include <endian.h>
14 #include <bionic_futex.h>
15 #include <bionic_atomic_inline.h>
16 
17 // ARM C++ ABI and Itanium/x86 C++ ABI has different definition for
18 // one time construction:
19 //
20 //    ARM C++ ABI defines the LSB of guard variable should be tested
21 //    by compiler-generated code before calling __cxa_guard_acquire et al.
22 //
23 //    The Itanium/x86 C++ ABI defines the low-order _byte_ should be
24 //    tested instead.
25 //
26 //    Meanwhile, guard variable are 32bit aligned for ARM, and 64bit
27 //    aligned for x86.
28 //
29 // Reference documentation:
30 //
31 //    section 3.2.3 of ARM IHI 0041C (for ARM)
32 //    section 3.3.2 of the Itanium C++ ABI specification v1.83 (for x86).
33 //
34 // There is no C++ ABI available for other ARCH. But the gcc source
35 // shows all other ARCH follow the definition of Itanium/x86 C++ ABI.
36 
37 
38 #if defined(__arm__)
39 // The ARM C++ ABI mandates that guard variable are
40 // 32-bit aligned, 32-bit values. And only its LSB is tested by
41 // the compiler-generated code before calling
42 // __cxa_guard_acquire.
43 //
44 typedef union {
45     int volatile state;
46     int32_t aligner;
47 } _guard_t;
48 
49 const static int ready = 0x1;
50 const static int pending = 0x2;
51 const static int waiting = 0x6;
52 
53 #else   // GCC sources indicates all none-arm follow the same ABI
54 // The Itanium/x86 C++ ABI mandates that guard variables
55 // are 64-bit aligned, 64-bit values. Also, the least-significant
56 // byte is tested by the compiler-generated code before, we calling
57 // __cxa_guard_acquire. We can access it through the first
58 // 32-bit word in the union below.
59 //
60 typedef union {
61     int volatile state;
62     int64_t aligner;
63 } _guard_t;
64 
65 const static int ready     = letoh32(0x1);
66 const static int pending   = letoh32(0x100);
67 const static int waiting   = letoh32(0x10000);
68 #endif
69 
__cxa_guard_acquire(_guard_t * gv)70 extern "C" int __cxa_guard_acquire(_guard_t* gv)
71 {
72     // 0 -> pending, return 1
73     // pending -> waiting, wait and return 0
74     // waiting: untouched, wait and return 0
75     // ready: untouched, return 0
76 
77 retry:
78     if (__bionic_cmpxchg(0, pending, &gv->state) == 0) {
79         ANDROID_MEMBAR_FULL();
80         return 1;
81     }
82     __bionic_cmpxchg(pending, waiting, &gv->state); // Indicate there is a waiter
83     __futex_wait(&gv->state, waiting, NULL);
84 
85     if (gv->state != ready) // __cxa_guard_abort was called, let every thread try since there is no return code for this condition
86         goto retry;
87 
88     ANDROID_MEMBAR_FULL();
89     return 0;
90 }
91 
__cxa_guard_release(_guard_t * gv)92 extern "C" void __cxa_guard_release(_guard_t* gv)
93 {
94     // pending -> ready
95     // waiting -> ready, and wake
96 
97     ANDROID_MEMBAR_FULL();
98     if (__bionic_cmpxchg(pending, ready, &gv->state) == 0) {
99         return;
100     }
101 
102     gv->state = ready;
103     __futex_wake(&gv->state, 0x7fffffff);
104 }
105 
__cxa_guard_abort(_guard_t * gv)106 extern "C" void __cxa_guard_abort(_guard_t* gv)
107 {
108     ANDROID_MEMBAR_FULL();
109     gv->state= 0;
110     __futex_wake(&gv->state, 0x7fffffff);
111 }
112