2008-05-30 Vladimir Makarov <vmakarov@redhat.com>
[official-gcc.git] / libstdc++-v3 / libsupc++ / guard.cc
blob312166201fbe0f6df99b0acf991c6125a97570e6
1 // Copyright (C) 2002, 2004, 2006, 2008 Free Software Foundation, Inc.
2 //
3 // This file is part of GCC.
4 //
5 // GCC is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2, or (at your option)
8 // any later version.
10 // GCC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with GCC; see the file COPYING. If not, write to
17 // the Free Software Foundation, 51 Franklin Street, Fifth Floor,
18 // Boston, MA 02110-1301, USA.
20 // As a special exception, you may use this file as part of a free software
21 // library without restriction. Specifically, if other files instantiate
22 // templates or use macros or inline functions from this file, or you compile
23 // this file and link it with other files to produce an executable, this
24 // file does not by itself cause the resulting executable to be covered by
25 // the GNU General Public License. This exception does not however
26 // invalidate any other reasons why the executable file might be covered by
27 // the GNU General Public License.
29 // Written by Mark Mitchell, CodeSourcery LLC, <mark@codesourcery.com>
30 // Thread support written by Jason Merrill, Red Hat Inc. <jason@redhat.com>
32 #include <bits/c++config.h>
33 #include <cxxabi.h>
34 #include <exception>
35 #include <new>
36 #include <ext/atomicity.h>
37 #include <ext/concurrence.h>
38 #if defined(__GTHREADS) && defined(__GTHREAD_HAS_COND) \
39 && defined(_GLIBCXX_ATOMIC_BUILTINS_4) && defined(_GLIBCXX_HAVE_LINUX_FUTEX)
40 # include <climits>
41 # include <syscall.h>
42 # define _GLIBCXX_USE_FUTEX
43 # define _GLIBCXX_FUTEX_WAIT 0
44 # define _GLIBCXX_FUTEX_WAKE 1
45 #endif
47 // The IA64/generic ABI uses the first byte of the guard variable.
48 // The ARM EABI uses the least significant bit.
50 // Thread-safe static local initialization support.
51 #ifdef __GTHREADS
52 # ifndef _GLIBCXX_USE_FUTEX
53 namespace
55 // A single mutex controlling all static initializations.
56 static __gnu_cxx::__recursive_mutex* static_mutex;
58 typedef char fake_recursive_mutex[sizeof(__gnu_cxx::__recursive_mutex)]
59 __attribute__ ((aligned(__alignof__(__gnu_cxx::__recursive_mutex))));
60 fake_recursive_mutex fake_mutex;
62 static void init()
63 { static_mutex = new (&fake_mutex) __gnu_cxx::__recursive_mutex(); }
65 __gnu_cxx::__recursive_mutex&
66 get_static_mutex()
68 static __gthread_once_t once = __GTHREAD_ONCE_INIT;
69 __gthread_once(&once, init);
70 return *static_mutex;
73 // Simple wrapper for exception safety.
74 struct mutex_wrapper
76 bool unlock;
77 mutex_wrapper() : unlock(true)
78 { get_static_mutex().lock(); }
80 ~mutex_wrapper()
82 if (unlock)
83 static_mutex->unlock();
87 # endif
89 # if defined(__GTHREAD_HAS_COND) && !defined(_GLIBCXX_USE_FUTEX)
90 namespace
92 // A single conditional variable controlling all static initializations.
93 static __gnu_cxx::__cond* static_cond;
95 // using a fake type to avoid initializing a static class.
96 typedef char fake_cond_t[sizeof(__gnu_cxx::__cond)]
97 __attribute__ ((aligned(__alignof__(__gnu_cxx::__cond))));
98 fake_cond_t fake_cond;
100 static void init_static_cond()
101 { static_cond = new (&fake_cond) __gnu_cxx::__cond(); }
103 __gnu_cxx::__cond&
104 get_static_cond()
106 static __gthread_once_t once = __GTHREAD_ONCE_INIT;
107 __gthread_once(&once, init_static_cond);
108 return *static_cond;
111 # endif
113 # ifndef _GLIBCXX_GUARD_TEST_AND_ACQUIRE
114 inline bool
115 __test_and_acquire (__cxxabiv1::__guard *g)
117 bool b = _GLIBCXX_GUARD_TEST (g);
118 _GLIBCXX_READ_MEM_BARRIER;
119 return b;
121 # define _GLIBCXX_GUARD_TEST_AND_ACQUIRE(G) __test_and_acquire (G)
122 # endif
124 # ifndef _GLIBCXX_GUARD_SET_AND_RELEASE
125 inline void
126 __set_and_release (__cxxabiv1::__guard *g)
128 _GLIBCXX_WRITE_MEM_BARRIER;
129 _GLIBCXX_GUARD_SET (g);
131 # define _GLIBCXX_GUARD_SET_AND_RELEASE(G) __set_and_release (G)
132 # endif
134 #else /* !__GTHREADS */
136 # undef _GLIBCXX_GUARD_TEST_AND_ACQUIRE
137 # undef _GLIBCXX_GUARD_SET_AND_RELEASE
138 # define _GLIBCXX_GUARD_SET_AND_RELEASE(G) _GLIBCXX_GUARD_SET (G)
140 #endif /* __GTHREADS */
142 namespace __gnu_cxx
144 // 6.7[stmt.dcl]/4: If control re-enters the declaration (recursively)
145 // while the object is being initialized, the behavior is undefined.
147 // Since we already have a library function to handle locking, we might
148 // as well check for this situation and throw an exception.
149 // We use the second byte of the guard variable to remember that we're
150 // in the middle of an initialization.
151 class recursive_init_error: public std::exception
153 public:
154 recursive_init_error() throw() { }
155 virtual ~recursive_init_error() throw ();
158 recursive_init_error::~recursive_init_error() throw() { }
162 // Here are C++ run-time routines for guarded initiailization of static
163 // variables. There are 4 scenarios under which these routines are called:
165 // 1. Threads not supported (__GTHREADS not defined)
166 // 2. Threads are supported but not enabled at run-time.
167 // 3. Threads enabled at run-time but __gthreads_* are not fully POSIX.
168 // 4. Threads enabled at run-time and __gthreads_* support all POSIX threads
169 // primitives we need here.
171 // The old code supported scenarios 1-3 but was broken since it used a global
172 // mutex for all threads and had the mutex locked during the whole duration of
173 // initlization of a guarded static variable. The following created a dead-lock
174 // with the old code.
176 // Thread 1 acquires the global mutex.
177 // Thread 1 starts initializing static variable.
178 // Thread 1 creates thread 2 during initialization.
179 // Thread 2 attempts to acuqire mutex to initialize another variable.
180 // Thread 2 blocks since thread 1 is locking the mutex.
181 // Thread 1 waits for result from thread 2 and also blocks. A deadlock.
183 // The new code here can handle this situation and thus is more robust. Howere,
184 // we need to use the POSIX thread conditional variable, which is not supported
185 // in all platforms, notably older versions of Microsoft Windows. The gthr*.h
186 // headers define a symbol __GTHREAD_HAS_COND for platforms that support POSIX
187 // like conditional variables. For platforms that do not support conditional
188 // variables, we need to fall back to the old code.
190 // If _GLIBCXX_USE_FUTEX, no global mutex or conditional variable is used,
191 // only atomic operations are used together with futex syscall.
192 // Valid values of the first integer in guard are:
193 // 0 No thread encountered the guarded init
194 // yet or it has been aborted.
195 // _GLIBCXX_GUARD_BIT The guarded static var has been successfully
196 // initialized.
197 // _GLIBCXX_GUARD_PENDING_BIT The guarded static var is being initialized
198 // and no other thread is waiting for its
199 // initialization.
200 // (_GLIBCXX_GUARD_PENDING_BIT The guarded static var is being initialized
201 // | _GLIBCXX_GUARD_WAITING_BIT) and some other threads are waiting until
202 // it is initialized.
204 namespace __cxxabiv1
206 #ifdef _GLIBCXX_USE_FUTEX
207 namespace
209 static inline int __guard_test_bit (const int __byte, const int __val)
211 union { int __i; char __c[sizeof (int)]; } __u = { 0 };
212 __u.__c[__byte] = __val;
213 return __u.__i;
216 #endif
218 static inline int
219 init_in_progress_flag(__guard* g)
220 { return ((char *)g)[1]; }
222 static inline void
223 set_init_in_progress_flag(__guard* g, int v)
224 { ((char *)g)[1] = v; }
226 static inline void
227 throw_recursive_init_exception()
229 #ifdef __EXCEPTIONS
230 throw __gnu_cxx::recursive_init_error();
231 #else
232 // Use __builtin_trap so we don't require abort().
233 __builtin_trap();
234 #endif
237 // acuire() is a helper function used to acquire guard if thread support is
238 // not compiled in or is compiled in but not enabled at run-time.
239 static int
240 acquire(__guard *g)
242 // Quit if the object is already initialized.
243 if (_GLIBCXX_GUARD_TEST(g))
244 return 0;
246 if (init_in_progress_flag(g))
247 throw_recursive_init_exception();
249 set_init_in_progress_flag(g, 1);
250 return 1;
253 extern "C"
254 int __cxa_guard_acquire (__guard *g)
256 #ifdef __GTHREADS
257 // If the target can reorder loads, we need to insert a read memory
258 // barrier so that accesses to the guarded variable happen after the
259 // guard test.
260 if (_GLIBCXX_GUARD_TEST_AND_ACQUIRE (g))
261 return 0;
263 # ifdef _GLIBCXX_USE_FUTEX
264 // If __sync_* and futex syscall are supported, don't use any global
265 // mutex.
266 if (__gthread_active_p ())
268 int *gi = (int *) (void *) g;
269 const int guard_bit = _GLIBCXX_GUARD_BIT;
270 const int pending_bit = _GLIBCXX_GUARD_PENDING_BIT;
271 const int waiting_bit = _GLIBCXX_GUARD_WAITING_BIT;
273 while (1)
275 int old = __sync_val_compare_and_swap (gi, 0, pending_bit);
276 if (old == 0)
277 return 1; // This thread should do the initialization.
279 if (old == guard_bit)
280 return 0; // Already initialized.
282 if (old == pending_bit)
284 int newv = old | waiting_bit;
285 if (__sync_val_compare_and_swap (gi, old, newv) != old)
286 continue;
288 old = newv;
291 syscall (SYS_futex, gi, _GLIBCXX_FUTEX_WAIT, old, 0);
294 # else
295 if (__gthread_active_p ())
297 mutex_wrapper mw;
299 while (1) // When this loop is executing, mutex is locked.
301 # ifdef __GTHREAD_HAS_COND
302 // The static is already initialized.
303 if (_GLIBCXX_GUARD_TEST(g))
304 return 0; // The mutex will be unlocked via wrapper
306 if (init_in_progress_flag(g))
308 // The guarded static is currently being initialized by
309 // another thread, so we release mutex and wait for the
310 // conditional variable. We will lock the mutex again after
311 // this.
312 get_static_cond().wait_recursive(&get_static_mutex());
314 else
316 set_init_in_progress_flag(g, 1);
317 return 1; // The mutex will be unlocked via wrapper.
319 # else
320 // This provides compatibility with older systems not supporting
321 // POSIX like conditional variables.
322 if (acquire(g))
324 mw.unlock = false;
325 return 1; // The mutex still locked.
327 return 0; // The mutex will be unlocked via wrapper.
328 # endif
331 # endif
332 #endif
334 return acquire (g);
337 extern "C"
338 void __cxa_guard_abort (__guard *g)
340 #ifdef _GLIBCXX_USE_FUTEX
341 // If __sync_* and futex syscall are supported, don't use any global
342 // mutex.
343 if (__gthread_active_p ())
345 int *gi = (int *) (void *) g;
346 const int waiting_bit = _GLIBCXX_GUARD_WAITING_BIT;
347 int old = __sync_lock_test_and_set (gi, 0);
349 if ((old & waiting_bit) != 0)
350 syscall (SYS_futex, gi, _GLIBCXX_FUTEX_WAKE, INT_MAX);
351 return;
353 #elif defined(__GTHREAD_HAS_COND)
354 if (__gthread_active_p())
356 mutex_wrapper mw;
358 set_init_in_progress_flag(g, 0);
360 // If we abort, we still need to wake up all other threads waiting for
361 // the conditional variable.
362 get_static_cond().broadcast();
363 return;
365 #endif
367 set_init_in_progress_flag(g, 0);
368 #if defined(__GTHREADS) && !defined(__GTHREAD_HAS_COND)
369 // This provides compatibility with older systems not supporting POSIX like
370 // conditional variables.
371 if (__gthread_active_p ())
372 static_mutex->unlock();
373 #endif
376 extern "C"
377 void __cxa_guard_release (__guard *g)
379 #ifdef _GLIBCXX_USE_FUTEX
380 // If __sync_* and futex syscall are supported, don't use any global
381 // mutex.
382 if (__gthread_active_p ())
384 int *gi = (int *) (void *) g;
385 const int guard_bit = _GLIBCXX_GUARD_BIT;
386 const int waiting_bit = _GLIBCXX_GUARD_WAITING_BIT;
387 int old = __sync_lock_test_and_set (gi, guard_bit);
389 if ((old & waiting_bit) != 0)
390 syscall (SYS_futex, gi, _GLIBCXX_FUTEX_WAKE, INT_MAX);
391 return;
393 #elif defined(__GTHREAD_HAS_COND)
394 if (__gthread_active_p())
396 mutex_wrapper mw;
398 set_init_in_progress_flag(g, 0);
399 _GLIBCXX_GUARD_SET_AND_RELEASE(g);
401 get_static_cond().broadcast();
402 return;
404 #endif
406 set_init_in_progress_flag(g, 0);
407 _GLIBCXX_GUARD_SET_AND_RELEASE (g);
409 #if defined(__GTHREADS) && !defined(__GTHREAD_HAS_COND)
410 // This provides compatibility with older systems not supporting POSIX like
411 // conditional variables.
412 if (__gthread_active_p())
413 static_mutex->unlock();
414 #endif