1 #include <NTL/config.h>
2 #include <NTL/new.h>
3 #include <atomic>
4 #include <thread>
5 #include <utility>
6 #include <cstdlib>
7
8 #include <iostream>
9
TerminalError(const char * s)10 void TerminalError(const char *s)
11 {
12 std::cerr << s << "\n";
13 std::abort();
14 }
15
MemoryError()16 void MemoryError() { TerminalError("out of memory"); }
ResourceError(const char * msg)17 void ResourceError(const char *msg) { TerminalError(msg); }
18
19
20 #if (defined(NTL_THREADS) && defined(NTL_TLS_HACK))
21 #include <pthread.h>
22 #endif
23
24 #define NTL_THREAD_LOCAL thread_local
25
26 #ifdef __GNUC__
27 #define NTL_CHEAP_THREAD_LOCAL __thread
28 #else
29 #define NTL_CHEAP_THREAD_LOCAL thread_local
30 #endif
31
32
33 #define NTL_DETAILS_PTHREAD details_pthread
34
35
36 #if (defined(NTL_THREADS) && defined(NTL_TLS_HACK))
37
38 namespace details_pthread {
39
40
41 struct Node {
42 Node *next;
43
Nodedetails_pthread::Node44 Node() : next(0) { }
~Nodedetails_pthread::Node45 virtual ~Node() { }
46 };
47
48 template<class T>
49 struct DerivedNode : Node {
50 T t;
51
52 template<class... Args>
DerivedNodedetails_pthread::DerivedNode53 DerivedNode(Args&&... args) : t(std::forward<Args>(args)...) { }
54 };
55
56 inline void
delete_node(Node * p)57 delete_node(Node *p) noexcept { delete p; }
58 // an exception here would likely lead to a complete mess...
59 // the noexcept specification should force an immediate termination
60
61 inline void
delete_list(void * vp)62 delete_list(void *vp)
63 {
64 Node *p = (Node *) vp;
65 while (p) {
66 Node *tmp = p;
67 p = p->next;
68 delete_node(tmp);
69 }
70 }
71
72
73 using namespace std;
74 // I'm not sure if pthread stuff might be placed in namespace std
75
76 struct key_wrapper {
77 pthread_key_t key;
78
key_wrapperdetails_pthread::key_wrapper79 key_wrapper(void (*destructor)(void*))
80 {
81 if (pthread_key_create(&key, destructor))
82 ResourceError("pthread_key_create failed");
83 }
84 };
85
86
87 inline void
push_node(Node * p)88 push_node(Node *p)
89 // this pushes a new node to the front to the list
90 // of objects that need to be deleted
91 {
92 if (!p) MemoryError();
93
94 static key_wrapper wkey(delete_list);
95 // This relies on C++11 thread-safe static initialization.
96 // It also relies on the guarantee that there is just one
97 // global key (this requirement is only needed to
98 // limit the number of keys, not for correctness).
99
100 p->next = (Node *) pthread_getspecific(wkey.key);
101
102 if (pthread_setspecific(wkey.key, p)) {
103 delete_node(p);
104 ResourceError("pthread_setspecific failed");
105 }
106 }
107
108 }
109
110
111 #define NTL_TLS_LOCAL_INIT(type, var, init) \
112 static NTL_CHEAP_THREAD_LOCAL NTL_DETAILS_PTHREAD::DerivedNode<type> *_ntl_hidden_variable_tls_local_ptr_ ## var = 0; \
113 NTL_DETAILS_PTHREAD::DerivedNode<type> *_ntl_hidden_variable_tls_local_ptr1_ ## var = _ntl_hidden_variable_tls_local_ptr_ ## var; \
114 if (!_ntl_hidden_variable_tls_local_ptr1_ ## var) { \
115 NTL_DETAILS_PTHREAD::DerivedNode<type> *_ntl_hidden_variable_tls_local_ptr2_ ## var = NTL_NEW_OP NTL_DETAILS_PTHREAD::DerivedNode<type> init; \
116 NTL_DETAILS_PTHREAD::push_node(_ntl_hidden_variable_tls_local_ptr2_ ## var); \
117 _ntl_hidden_variable_tls_local_ptr1_ ## var = _ntl_hidden_variable_tls_local_ptr2_ ## var; \
118 _ntl_hidden_variable_tls_local_ptr_ ## var = _ntl_hidden_variable_tls_local_ptr1_ ## var; \
119 } \
120 type &var = _ntl_hidden_variable_tls_local_ptr1_ ## var->t \
121
122
123
124 #else
125
126
127 // NOTE: this definition of NTL_TLS_LOCAL_INIT ensures that var names
128 // a local reference, regardless of the implementation
129 #define NTL_TLS_LOCAL_INIT(type,var,init) \
130 static NTL_THREAD_LOCAL type _ntl_hidden_variable_tls_local ## var init; \
131 type &var = _ntl_hidden_variable_tls_local ## var
132
133
134
135
136 #endif
137
138 #define NTL_EMPTY_ARG
139 #define NTL_TLS_LOCAL(type,var) NTL_TLS_LOCAL_INIT(type,var,NTL_EMPTY_ARG)
140
141 #define NTL_TLS_GLOBAL_DECL_INIT(type,var,init) \
142 typedef type _ntl_hidden_typedef_tls_access_ ## var; \
143 static inline \
144 type& _ntl_hidden_function_tls_access_ ## var() { \
145 NTL_TLS_LOCAL_INIT(type,var,init); \
146 return var; \
147 } \
148
149
150 #define NTL_TLS_GLOBAL_DECL(type,var) NTL_TLS_GLOBAL_DECL_INIT(type,var,NTL_EMPTY_ARG)
151
152 #define NTL_TLS_GLOBAL_ACCESS(var) \
153 _ntl_hidden_typedef_tls_access_ ## var & var = _ntl_hidden_function_tls_access_ ## var()
154
155
156 //=======================
157
158 std::atomic_long count_con(0);
159 std::atomic_long count_des(0);
160 std::atomic_long count1(0);
161
162 struct X {
163 long d;
164
XX165 X() { d = count1++; count_con++; }
~XX166 ~X() { count_des++; }
167 };
168
NTL_TLS_GLOBAL_DECL(X,x)169 NTL_TLS_GLOBAL_DECL(X,x)
170
171 void task(long *v)
172 {
173 NTL_TLS_GLOBAL_ACCESS(x);
174 *v = x.d;
175 }
176
177
178 #define MIN(a,b) ((a)<(b)?(a):(b))
179 #define MAX(a,b) ((a)>(b)?(a):(b))
180
main()181 int main()
182 {
183 long v1, v2, v3;
184 std::thread t1(task, &v1);
185 std::thread t2(task, &v2);
186 std::thread t3(task, &v3);
187
188 t1.join();
189 t2.join();
190 t3.join();
191
192 //std::cout << count_con << "\n";
193 //std::cout << count_des << "\n";
194 //std::cout << v1 << " " << v2 << " " << v3 << "\n";
195
196 long s1, s2, s3;
197 s1 = MIN(MIN(v1,v2),v3);
198 s3 = MAX(MAX(v1,v2),v3);
199 s2 = v1+v2+v3-s1-s3;
200
201 if (count_con != 3 || count_des != 3 || s1 != 0 || s2 != 1 || s3 != 2) {
202 return -1;
203 }
204 return 0;
205 }
206