#include <bits/c++config.h>
#include <cxxabi.h>
#include <exception>
#include <new>
#include <ext/atomicity.h>
#include <ext/concurrence.h>
#if defined(__GTHREADS) && defined(__GTHREAD_HAS_COND) \
&& defined(_GLIBCXX_ATOMIC_BUILTINS_4) && defined(_GLIBCXX_HAVE_LINUX_FUTEX)
# include <climits>
# include <syscall.h>
# define _GLIBCXX_USE_FUTEX
# define _GLIBCXX_FUTEX_WAIT 0
# define _GLIBCXX_FUTEX_WAKE 1
#endif
#ifdef __GTHREADS
# ifndef _GLIBCXX_USE_FUTEX
namespace
{
static __gnu_cxx::__recursive_mutex* static_mutex;
typedef char fake_recursive_mutex[sizeof(__gnu_cxx::__recursive_mutex)]
__attribute__ ((aligned(__alignof__(__gnu_cxx::__recursive_mutex))));
fake_recursive_mutex fake_mutex;
static void init()
{ static_mutex = new (&fake_mutex) __gnu_cxx::__recursive_mutex(); }
__gnu_cxx::__recursive_mutex&
get_static_mutex()
{
static __gthread_once_t once = __GTHREAD_ONCE_INIT;
__gthread_once(&once, init);
return *static_mutex;
}
struct mutex_wrapper
{
bool unlock;
mutex_wrapper() : unlock(true)
{ get_static_mutex().lock(); }
~mutex_wrapper()
{
if (unlock)
static_mutex->unlock();
}
};
}
# endif
# if defined(__GTHREAD_HAS_COND) && !defined(_GLIBCXX_USE_FUTEX)
namespace
{
static __gnu_cxx::__cond* static_cond;
typedef char fake_cond_t[sizeof(__gnu_cxx::__cond)]
__attribute__ ((aligned(__alignof__(__gnu_cxx::__cond))));
fake_cond_t fake_cond;
static void init_static_cond()
{ static_cond = new (&fake_cond) __gnu_cxx::__cond(); }
__gnu_cxx::__cond&
get_static_cond()
{
static __gthread_once_t once = __GTHREAD_ONCE_INIT;
__gthread_once(&once, init_static_cond);
return *static_cond;
}
}
# endif
# ifndef _GLIBCXX_GUARD_TEST_AND_ACQUIRE
inline bool
__test_and_acquire (__cxxabiv1::__guard *g)
{
bool b = _GLIBCXX_GUARD_TEST (g);
_GLIBCXX_READ_MEM_BARRIER;
return b;
}
# define _GLIBCXX_GUARD_TEST_AND_ACQUIRE(G) __test_and_acquire (G)
# endif
# ifndef _GLIBCXX_GUARD_SET_AND_RELEASE
inline void
__set_and_release (__cxxabiv1::__guard *g)
{
_GLIBCXX_WRITE_MEM_BARRIER;
_GLIBCXX_GUARD_SET (g);
}
# define _GLIBCXX_GUARD_SET_AND_RELEASE(G) __set_and_release (G)
# endif
#else
# undef _GLIBCXX_GUARD_TEST_AND_ACQUIRE
# undef _GLIBCXX_GUARD_SET_AND_RELEASE
# define _GLIBCXX_GUARD_SET_AND_RELEASE(G) _GLIBCXX_GUARD_SET (G)
#endif
namespace __gnu_cxx
{
class recursive_init_error: public std::exception
{
public:
recursive_init_error() throw() { }
virtual ~recursive_init_error() throw ();
};
recursive_init_error::~recursive_init_error() throw() { }
}
namespace __cxxabiv1
{
#ifdef _GLIBCXX_USE_FUTEX
namespace
{
static inline int __guard_test_bit (const int __byte, const int __val)
{
union { int __i; char __c[sizeof (int)]; } __u = { 0 };
__u.__c[__byte] = __val;
return __u.__i;
}
}
#endif
static inline int
init_in_progress_flag(__guard* g)
{ return ((char *)g)[1]; }
static inline void
set_init_in_progress_flag(__guard* g, int v)
{ ((char *)g)[1] = v; }
static inline void
throw_recursive_init_exception()
{
#ifdef __EXCEPTIONS
throw __gnu_cxx::recursive_init_error();
#else
__builtin_trap();
#endif
}
static int
acquire(__guard *g)
{
if (_GLIBCXX_GUARD_TEST(g))
return 0;
if (init_in_progress_flag(g))
throw_recursive_init_exception();
set_init_in_progress_flag(g, 1);
return 1;
}
extern "C"
int __cxa_guard_acquire (__guard *g)
{
#ifdef __GTHREADS
if (_GLIBCXX_GUARD_TEST_AND_ACQUIRE (g))
return 0;
# ifdef _GLIBCXX_USE_FUTEX
if (__gthread_active_p ())
{
int *gi = (int *) (void *) g;
const int guard_bit = _GLIBCXX_GUARD_BIT;
const int pending_bit = _GLIBCXX_GUARD_PENDING_BIT;
const int waiting_bit = _GLIBCXX_GUARD_WAITING_BIT;
while (1)
{
int old = __sync_val_compare_and_swap (gi, 0, pending_bit);
if (old == 0)
return 1;
if (old == guard_bit)
return 0;
if (old == pending_bit)
{
int newv = old | waiting_bit;
if (__sync_val_compare_and_swap (gi, old, newv) != old)
continue;
old = newv;
}
syscall (SYS_futex, gi, _GLIBCXX_FUTEX_WAIT, old, 0);
}
}
# else
if (__gthread_active_p ())
{
mutex_wrapper mw;
while (1)
{
# ifdef __GTHREAD_HAS_COND
if (_GLIBCXX_GUARD_TEST(g))
return 0;
if (init_in_progress_flag(g))
{
get_static_cond().wait_recursive(&get_static_mutex());
}
else
{
set_init_in_progress_flag(g, 1);
return 1;
}
# else
if (acquire(g))
{
mw.unlock = false;
return 1;
}
return 0;
# endif
}
}
# endif
#endif
return acquire (g);
}
extern "C"
void __cxa_guard_abort (__guard *g)
{
#ifdef _GLIBCXX_USE_FUTEX
if (__gthread_active_p ())
{
int *gi = (int *) (void *) g;
const int waiting_bit = _GLIBCXX_GUARD_WAITING_BIT;
int old = __sync_lock_test_and_set (gi, 0);
if ((old & waiting_bit) != 0)
syscall (SYS_futex, gi, _GLIBCXX_FUTEX_WAKE, INT_MAX);
return;
}
#elif defined(__GTHREAD_HAS_COND)
if (__gthread_active_p())
{
mutex_wrapper mw;
set_init_in_progress_flag(g, 0);
get_static_cond().broadcast();
return;
}
#endif
set_init_in_progress_flag(g, 0);
#if defined(__GTHREADS) && !defined(__GTHREAD_HAS_COND)
if (__gthread_active_p ())
static_mutex->unlock();
#endif
}
extern "C"
void __cxa_guard_release (__guard *g)
{
#ifdef _GLIBCXX_USE_FUTEX
if (__gthread_active_p ())
{
int *gi = (int *) (void *) g;
const int guard_bit = _GLIBCXX_GUARD_BIT;
const int waiting_bit = _GLIBCXX_GUARD_WAITING_BIT;
int old = __sync_lock_test_and_set (gi, guard_bit);
if ((old & waiting_bit) != 0)
syscall (SYS_futex, gi, _GLIBCXX_FUTEX_WAKE, INT_MAX);
return;
}
#elif defined(__GTHREAD_HAS_COND)
if (__gthread_active_p())
{
mutex_wrapper mw;
set_init_in_progress_flag(g, 0);
_GLIBCXX_GUARD_SET_AND_RELEASE(g);
get_static_cond().broadcast();
return;
}
#endif
set_init_in_progress_flag(g, 0);
_GLIBCXX_GUARD_SET_AND_RELEASE (g);
#if defined(__GTHREADS) && !defined(__GTHREAD_HAS_COND)
if (__gthread_active_p())
static_mutex->unlock();
#endif
}
}