gthr-posix.h (__gthread_cond_broadcast, [...]): Add to extend interface for POSIX conditional variables.
2007-09-13 Doug Kwan <dougkwan@google.com> * gcc/gthr-posix.h (__gthread_cond_broadcast, __gthread_cond_wait, __gthread_cond_wait_recursive): Add to extend interface for POSIX conditional variables. (__GTHREAD_HAS_COND): Macro defined to signify support of conditional variables. * gcc/gthr-posix95.h (__gthread_cond_broadcast, __gthread_cond_wait, __gthread_cond_wait_recursive): Add to extend interface for POSIX conditional variables. (__GTHREAD_HAS_COND): Macro defined to signify support of conditional variables. * gcc/gthr-single.h (__gthread_cond_broadcast, __gthread_cond_wait, __gthread_cond_wait_recursive): Add to extend interface for POSIX conditional variables. * gcc/gthr.h: Update comments to document new interface. * libstdc++-v3/include/ext/concurrent.h (class __mutex, class __recursive_mutex): Add new method gthread_mutex to access inner gthread mutex. [__GTHREAD_HAS_COND] (class __concurrence_broadcast_error, class __concurrence_wait_error, class __cond): Add. * guard.cc (recursive_push, recursive_pop): Delete. (init_in_progress_flag, set_init_in_progress_flag): Add to replace recursive_push and recursive_pop. (throw_recursive_init_exception): Add. (acquire, __cxa_guard_acquire, __cxa_guard_abort and __cxa_guard_release): [__GTHREAD_HAS_COND] Use a conditional for synchronization of static variable initialization. The global mutex is only held briefly when guards are accessed. [!__GTHREAD_HAS_COND] Fall back to the old code, which deadlocks. * testsuite/thread/guard.cc: Add new test. It deadlocks with the old locking code in libstdc++-v3/libsup++/guard.cc. From-SVN: r129030
This commit is contained in:
parent
90e965bb84
commit
afd82ef5ab
@ -1,3 +1,18 @@
|
||||
2007-10-04 Doug Kwan <dougkwan@google.com>
|
||||
|
||||
* gthr-posix.h (__gthread_cond_broadcast, __gthread_cond_wait,
|
||||
__gthread_cond_wait_recursive): Add to extend interface for POSIX
|
||||
conditional variables. (__GTHREAD_HAS_COND): Macro defined to signify
|
||||
support of conditional variables.
|
||||
* gthr-posix95.h (__gthread_cond_broadcast, __gthread_cond_wait,
|
||||
__gthread_cond_wait_recursive): Add to extend interface for POSIX
|
||||
conditional variables. (__GTHREAD_HAS_COND): Macro defined to signify
|
||||
support of conditional variables.
|
||||
* gthr-single.h (__gthread_cond_broadcast, __gthread_cond_wait,
|
||||
__gthread_cond_wait_recursive): Add to extend interface for POSIX
|
||||
conditional variables.
|
||||
* gthr.h: Update comments to document new interface.
|
||||
|
||||
2007-10-04 Geoffrey Keating <geoffk@apple.com>
|
||||
|
||||
* cgraphunit.c (cgraph_build_static_cdtor): Don't set
|
||||
|
@ -47,6 +47,11 @@ typedef pthread_key_t __gthread_key_t;
|
||||
typedef pthread_once_t __gthread_once_t;
|
||||
typedef pthread_mutex_t __gthread_mutex_t;
|
||||
typedef pthread_mutex_t __gthread_recursive_mutex_t;
|
||||
typedef pthread_cond_t __gthread_cond_t;
|
||||
|
||||
/* POSIX like conditional variables are supported. Please look at comments
|
||||
in gthr.h for details. */
|
||||
#define __GTHREAD_HAS_COND 1
|
||||
|
||||
#define __GTHREAD_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER
|
||||
#define __GTHREAD_ONCE_INIT PTHREAD_ONCE_INIT
|
||||
@ -57,6 +62,7 @@ typedef pthread_mutex_t __gthread_recursive_mutex_t;
|
||||
#else
|
||||
#define __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION __gthread_recursive_mutex_init_function
|
||||
#endif
|
||||
#define __GTHREAD_COND_INIT PTHREAD_COND_INITIALIZER
|
||||
|
||||
#if SUPPORTS_WEAK && GTHREAD_USE_WEAK
|
||||
# ifndef __gthrw_pragma
|
||||
@ -88,6 +94,8 @@ __gthrw3(pthread_mutex_lock)
|
||||
__gthrw3(pthread_mutex_trylock)
|
||||
__gthrw3(pthread_mutex_unlock)
|
||||
__gthrw3(pthread_mutex_init)
|
||||
__gthrw3(pthread_cond_broadcast)
|
||||
__gthrw3(pthread_cond_wait)
|
||||
#else
|
||||
__gthrw(pthread_once)
|
||||
__gthrw(pthread_getspecific)
|
||||
@ -98,6 +106,8 @@ __gthrw(pthread_mutex_lock)
|
||||
__gthrw(pthread_mutex_trylock)
|
||||
__gthrw(pthread_mutex_unlock)
|
||||
__gthrw(pthread_mutex_init)
|
||||
__gthrw(pthread_cond_broadcast)
|
||||
__gthrw(pthread_cond_wait)
|
||||
#endif
|
||||
|
||||
__gthrw(pthread_key_create)
|
||||
@ -110,20 +120,16 @@ __gthrw(pthread_mutexattr_destroy)
|
||||
#if defined(_LIBOBJC) || defined(_LIBOBJC_WEAK)
|
||||
/* Objective-C. */
|
||||
#if defined(__osf__) && defined(_PTHREAD_USE_MANGLED_NAMES_)
|
||||
__gthrw3(pthread_cond_broadcast)
|
||||
__gthrw3(pthread_cond_destroy)
|
||||
__gthrw3(pthread_cond_init)
|
||||
__gthrw3(pthread_cond_signal)
|
||||
__gthrw3(pthread_cond_wait)
|
||||
__gthrw3(pthread_exit)
|
||||
__gthrw3(pthread_mutex_destroy)
|
||||
__gthrw3(pthread_self)
|
||||
#else
|
||||
__gthrw(pthread_cond_broadcast)
|
||||
__gthrw(pthread_cond_destroy)
|
||||
__gthrw(pthread_cond_init)
|
||||
__gthrw(pthread_cond_signal)
|
||||
__gthrw(pthread_cond_wait)
|
||||
__gthrw(pthread_exit)
|
||||
__gthrw(pthread_mutex_destroy)
|
||||
__gthrw(pthread_self)
|
||||
@ -737,6 +743,25 @@ __gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex)
|
||||
return __gthread_mutex_unlock (mutex);
|
||||
}
|
||||
|
||||
static inline int
|
||||
__gthread_cond_broadcast (__gthread_cond_t *cond)
|
||||
{
|
||||
return __gthrw_(pthread_cond_broadcast) (cond);
|
||||
}
|
||||
|
||||
static inline int
|
||||
__gthread_cond_wait (__gthread_cond_t *cond, __gthread_mutex_t *mutex)
|
||||
{
|
||||
return __gthrw_(pthread_cond_wait) (cond, mutex);
|
||||
}
|
||||
|
||||
static inline int
|
||||
__gthread_cond_wait_recursive (__gthread_cond_t *cond,
|
||||
__gthread_recursive_mutex_t *mutex)
|
||||
{
|
||||
return __gthread_cond_wait (cond, mutex);
|
||||
}
|
||||
|
||||
#endif /* _LIBOBJC */
|
||||
|
||||
#endif /* ! GCC_GTHR_POSIX_H */
|
||||
|
@ -45,6 +45,11 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
typedef pthread_key_t __gthread_key_t;
|
||||
typedef pthread_once_t __gthread_once_t;
|
||||
typedef pthread_mutex_t __gthread_mutex_t;
|
||||
typedef pthread_cond_t __gthread_cond_t;
|
||||
|
||||
/* POSIX like conditional variables are supported. Please look at comments
|
||||
in gthr.h for details. */
|
||||
#define __GTHREAD_HAS_COND 1
|
||||
|
||||
typedef struct {
|
||||
long depth;
|
||||
@ -55,6 +60,7 @@ typedef struct {
|
||||
#define __GTHREAD_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER
|
||||
#define __GTHREAD_ONCE_INIT PTHREAD_ONCE_INIT
|
||||
#define __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION __gthread_recursive_mutex_init_function
|
||||
#define __GTHREAD_COND_INIT PTHREAD_COND_INITIALIZER
|
||||
|
||||
#if SUPPORTS_WEAK && GTHREAD_USE_WEAK
|
||||
# define __gthrw(name) \
|
||||
@ -81,14 +87,14 @@ __gthrw(pthread_mutexattr_init)
|
||||
__gthrw(pthread_mutexattr_destroy)
|
||||
|
||||
__gthrw(pthread_mutex_init)
|
||||
__gthrw(pthread_cond_broadcast)
|
||||
__gthrw(pthread_cond_wait)
|
||||
|
||||
#if defined(_LIBOBJC) || defined(_LIBOBJC_WEAK)
|
||||
/* Objective-C. */
|
||||
__gthrw(pthread_cond_broadcast)
|
||||
__gthrw(pthread_cond_destroy)
|
||||
__gthrw(pthread_cond_init)
|
||||
__gthrw(pthread_cond_signal)
|
||||
__gthrw(pthread_cond_wait)
|
||||
__gthrw(pthread_exit)
|
||||
__gthrw(pthread_mutex_destroy)
|
||||
#ifdef _POSIX_PRIORITY_SCHEDULING
|
||||
@ -719,6 +725,25 @@ __gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
__gthread_cond_broadcast (__gthread_cond_t *cond)
|
||||
{
|
||||
return __gthrw_(pthread_cond_broadcast) (cond);
|
||||
}
|
||||
|
||||
static inline int
|
||||
__gthread_cond_wait (__gthread_cond_t *cond, __gthread_mutex_t *mutex)
|
||||
{
|
||||
return __gthrw_(pthread_cond_wait) (cond, mutex);
|
||||
}
|
||||
|
||||
static inline int
|
||||
__gthread_cond_wait_recursive (__gthread_cond_t *cond,
|
||||
__gthread_recursive_mutex_t *mutex)
|
||||
{
|
||||
return __gthrw_(pthread_cond_wait) (cond, mutex->actual);
|
||||
}
|
||||
|
||||
#endif /* _LIBOBJC */
|
||||
|
||||
#endif /* ! GCC_GTHR_POSIX_H */
|
||||
|
@ -251,6 +251,25 @@ __gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex)
|
||||
return __gthread_mutex_unlock (mutex);
|
||||
}
|
||||
|
||||
static inline int
|
||||
__gthread_cond_broadcast (__gthread_cond_t cond)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
__gthread_cond_wait (__gthread_cond_t cond, __gthread_mutex_t *mutex)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
__gthread_cond_wait_recursive (__gthread_cond_t cond,
|
||||
__gthread_recursive_mutex_t *mutex)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* _LIBOBJC */
|
||||
|
||||
#undef UNUSED
|
||||
|
18
gcc/gthr.h
18
gcc/gthr.h
@ -81,6 +81,24 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
int __gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *mutex);
|
||||
int __gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex);
|
||||
|
||||
The following are supported in POSIX threads only. They are required to
|
||||
fix a deadlock in static initialization inside libsupc++. The header file
|
||||
gthr-posix.h defines a symbol __GTHREAD_HAS_COND to signify that these extra
|
||||
features are supported.
|
||||
|
||||
Types:
|
||||
__gthread_cond_t
|
||||
|
||||
Macros:
|
||||
__GTHREAD_COND_INIT
|
||||
__GTHREAD_COND_INIT_FUNCTION
|
||||
|
||||
Interface:
|
||||
int __gthread_cond_broadcast (__gthread_cond_t *cond);
|
||||
int __gthread_cond_wait (__gthread_cond_t *cond, __gthread_mutex_t *mutex);
|
||||
int __gthread_cond_wait_recursive (__gthread_cond_t *cond,
|
||||
__gthread_recursive_mutex_t *mutex);
|
||||
|
||||
All functions returning int should return zero on success or the error
|
||||
number. If the operation is not supported, -1 is returned.
|
||||
|
||||
|
@ -1,3 +1,23 @@
|
||||
2007-10-04 Doug Kwan <dougkwan@google.com>
|
||||
|
||||
* include/ext/concurrent.h (class __mutex,
|
||||
class __recursive_mutex): Add new method gthread_mutex to access
|
||||
inner gthread mutex.
|
||||
[__GTHREAD_HAS_COND] (class __concurrence_broadcast_error,
|
||||
class __concurrence_wait_error, class __cond): Add.
|
||||
* libsupc++/guard.cc (recursive_push, recursive_pop): Delete.
|
||||
(init_in_progress_flag, set_init_in_progress_flag): Add to
|
||||
replace recursive_push and recursive_pop.
|
||||
(throw_recursive_init_exception): Add.
|
||||
(acquire, __cxa_guard_acquire, __cxa_guard_abort and
|
||||
__cxa_guard_release): [__GTHREAD_HAS_COND] Use a conditional
|
||||
for synchronization of static variable initialization.
|
||||
The global mutex is only held briefly when guards are
|
||||
accessed. [!__GTHREAD_HAS_COND] Fall back to the old code,
|
||||
which deadlocks.
|
||||
* testsuite/thread/guard.cc: Add new test. It deadlocks with the
|
||||
old locking code in libstdc++-v3/libsup++/guard.cc.
|
||||
|
||||
2007-10-04 Paolo Carlini <pcarlini@suse.de>
|
||||
|
||||
* testsuite/23_containers/vector/requirements/dr438/assign_neg.cc:
|
||||
|
@ -81,6 +81,22 @@ _GLIBCXX_BEGIN_NAMESPACE(__gnu_cxx)
|
||||
{ return "__gnu_cxx::__concurrence_unlock_error"; }
|
||||
};
|
||||
|
||||
class __concurrence_broadcast_error : public std::exception
|
||||
{
|
||||
public:
|
||||
virtual char const*
|
||||
what() const throw()
|
||||
{ return "__gnu_cxx::__concurrence_broadcast_error"; }
|
||||
};
|
||||
|
||||
class __concurrence_wait_error : public std::exception
|
||||
{
|
||||
public:
|
||||
virtual char const*
|
||||
what() const throw()
|
||||
{ return "__gnu_cxx::__concurrence_wait_error"; }
|
||||
};
|
||||
|
||||
// Substitute for concurrence_error object in the case of -fno-exceptions.
|
||||
inline void
|
||||
__throw_concurrence_lock_error()
|
||||
@ -102,6 +118,28 @@ _GLIBCXX_BEGIN_NAMESPACE(__gnu_cxx)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __GTHREAD_HAS_COND
|
||||
inline void
|
||||
__throw_concurrence_broadcast_error()
|
||||
{
|
||||
#if __EXCEPTIONS
|
||||
throw __concurrence_broadcast_error();
|
||||
#else
|
||||
__builtin_abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void
|
||||
__throw_concurrence_wait_error()
|
||||
{
|
||||
#if __EXCEPTIONS
|
||||
throw __concurrence_wait_error();
|
||||
#else
|
||||
__builtin_abort();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
class __mutex
|
||||
{
|
||||
private:
|
||||
@ -147,6 +185,9 @@ _GLIBCXX_BEGIN_NAMESPACE(__gnu_cxx)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
__gthread_mutex_t* gthread_mutex(void)
|
||||
{ return &_M_mutex; }
|
||||
};
|
||||
|
||||
class __recursive_mutex
|
||||
@ -194,6 +235,9 @@ _GLIBCXX_BEGIN_NAMESPACE(__gnu_cxx)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
__gthread_recursive_mutex_t* gthread_recursive_mutex(void)
|
||||
{ return &_M_mutex; }
|
||||
};
|
||||
|
||||
/// @brief Scoped lock idiom.
|
||||
@ -218,6 +262,66 @@ _GLIBCXX_BEGIN_NAMESPACE(__gnu_cxx)
|
||||
{ _M_device.unlock(); }
|
||||
};
|
||||
|
||||
#ifdef __GTHREAD_HAS_COND
|
||||
class __cond
|
||||
{
|
||||
private:
|
||||
__gthread_cond_t _M_cond;
|
||||
|
||||
__cond(const __cond&);
|
||||
__cond& operator=(const __cond&);
|
||||
|
||||
public:
|
||||
__cond()
|
||||
{
|
||||
#if __GTHREADS
|
||||
if (__gthread_active_p())
|
||||
{
|
||||
#if defined __GTHREAD_COND_INIT
|
||||
__gthread_cond_t __tmp = __GTHREAD_COND_INIT;
|
||||
_M_cond = __tmp;
|
||||
#else
|
||||
__GTHREAD_MUTEX_INIT_FUNCTION(&_M_cond);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void broadcast()
|
||||
{
|
||||
#if __GTHREADS
|
||||
if (__gthread_active_p())
|
||||
{
|
||||
if (__gthread_cond_broadcast(&_M_cond) != 0)
|
||||
__throw_concurrence_broadcast_error();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void wait(__mutex *mutex)
|
||||
{
|
||||
#if __GTHREADS
|
||||
{
|
||||
if (__gthread_cond_wait(&_M_cond, mutex->gthread_mutex()) != 0)
|
||||
__throw_concurrence_wait_error();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void wait_recursive(__recursive_mutex *mutex)
|
||||
{
|
||||
#if __GTHREADS
|
||||
{
|
||||
if (__gthread_cond_wait_recursive(&_M_cond,
|
||||
mutex->gthread_recursive_mutex())
|
||||
!= 0)
|
||||
__throw_concurrence_wait_error();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
_GLIBCXX_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
@ -62,6 +62,28 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// A single conditional variable controlling all static initializations.
|
||||
static __gnu_cxx::__cond* static_cond;
|
||||
|
||||
// using a fake type to avoid initializing a static class.
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _GLIBCXX_GUARD_TEST_AND_ACQUIRE
|
||||
inline bool
|
||||
__test_and_acquire (__cxxabiv1::__guard *g)
|
||||
@ -110,34 +132,87 @@ namespace __gnu_cxx
|
||||
recursive_init_error::~recursive_init_error() throw() { }
|
||||
}
|
||||
|
||||
//
|
||||
// Here are C++ run-time routines for guarded initiailization of static
|
||||
// variables. There are 4 scenarios under which these routines are called:
|
||||
//
|
||||
// 1. Threads not supported (__GTHREADS not defined)
|
||||
// 2. Threads are supported but not enabled at run-time.
|
||||
// 3. Threads enabled at run-time but __gthreads_* are not fully POSIX.
|
||||
// 4. Threads enabled at run-time and __gthreads_* support all POSIX threads
|
||||
// primitives we need here.
|
||||
//
|
||||
// The old code supported scenarios 1-3 but was broken since it used a global
|
||||
// mutex for all threads and had the mutex locked during the whole duration of
|
||||
// initlization of a guarded static variable. The following created a dead-lock
|
||||
// with the old code.
|
||||
//
|
||||
// Thread 1 acquires the global mutex.
|
||||
// Thread 1 starts initializing static variable.
|
||||
// Thread 1 creates thread 2 during initialization.
|
||||
// Thread 2 attempts to acuqire mutex to initialize another variable.
|
||||
// Thread 2 blocks since thread 1 is locking the mutex.
|
||||
// Thread 1 waits for result from thread 2 and also blocks. A deadlock.
|
||||
//
|
||||
// The new code here can handle this situation and thus is more robust. Howere,
|
||||
// we need to use the POSIX thread conditional variable, which is not supported
|
||||
// in all platforms, notably older versions of Microsoft Windows. The gthr*.h
|
||||
// headers define a symbol __GTHREAD_HAS_COND for platforms that support POSIX
|
||||
// like conditional variables. For platforms that do not support conditional
|
||||
// variables, we need to fall back to the old code.
|
||||
namespace __cxxabiv1
|
||||
{
|
||||
static inline int
|
||||
recursion_push (__guard* g)
|
||||
{ return ((char *)g)[1]++; }
|
||||
init_in_progress_flag(__guard* g)
|
||||
{ return ((char *)g)[1]; }
|
||||
|
||||
static inline void
|
||||
recursion_pop (__guard* g)
|
||||
{ --((char *)g)[1]; }
|
||||
set_init_in_progress_flag(__guard* g, int v)
|
||||
{ ((char *)g)[1] = v; }
|
||||
|
||||
static int
|
||||
acquire (__guard *g)
|
||||
static inline void
|
||||
throw_recursive_init_exception()
|
||||
{
|
||||
if (_GLIBCXX_GUARD_TEST (g))
|
||||
return 0;
|
||||
|
||||
if (recursion_push (g))
|
||||
{
|
||||
#ifdef __EXCEPTIONS
|
||||
throw __gnu_cxx::recursive_init_error();
|
||||
#else
|
||||
// Use __builtin_trap so we don't require abort().
|
||||
__builtin_trap ();
|
||||
__builtin_trap();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// acuire() is a helper function used to acquire guard if thread support is
|
||||
// not compiled in or is compiled in but not enabled at run-time.
|
||||
static int
|
||||
acquire(__guard *g)
|
||||
{
|
||||
// Quit if the object is already initialized.
|
||||
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;
|
||||
}
|
||||
|
||||
// Simple wrapper for exception safety.
|
||||
struct mutex_wrapper
|
||||
{
|
||||
#ifdef __GTHREADS
|
||||
bool unlock;
|
||||
mutex_wrapper() : unlock(true)
|
||||
{ get_static_mutex().lock(); }
|
||||
|
||||
~mutex_wrapper()
|
||||
{
|
||||
if (unlock)
|
||||
static_mutex->unlock();
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
extern "C"
|
||||
int __cxa_guard_acquire (__guard *g)
|
||||
{
|
||||
@ -150,28 +225,39 @@ namespace __cxxabiv1
|
||||
|
||||
if (__gthread_active_p ())
|
||||
{
|
||||
// Simple wrapper for exception safety.
|
||||
struct mutex_wrapper
|
||||
{
|
||||
bool unlock;
|
||||
mutex_wrapper() : unlock(true)
|
||||
{ get_static_mutex().lock(); }
|
||||
|
||||
~mutex_wrapper()
|
||||
{
|
||||
if (unlock)
|
||||
static_mutex->unlock();
|
||||
}
|
||||
};
|
||||
|
||||
mutex_wrapper mw;
|
||||
if (acquire (g))
|
||||
{
|
||||
mw.unlock = false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
while (1) // When this loop is executing, mutex is locked.
|
||||
{
|
||||
#ifdef __GTHREAD_HAS_COND
|
||||
// The static is allready initialized.
|
||||
if (_GLIBCXX_GUARD_TEST(g))
|
||||
return 0; // The mutex will be unlocked via wrapper
|
||||
|
||||
if (init_in_progress_flag(g))
|
||||
{
|
||||
// The guarded static is currently being initialized by
|
||||
// another thread, so we release mutex and wait for the
|
||||
// conditional variable. We will lock the mutex again after
|
||||
// this.
|
||||
get_static_cond().wait_recursive(&get_static_mutex());
|
||||
}
|
||||
else
|
||||
{
|
||||
set_init_in_progress_flag(g, 1);
|
||||
return 1; // The mutex will be unlocked via wrapper.
|
||||
}
|
||||
#else
|
||||
// This provides compatibility with older systems not supporting
|
||||
// POSIX like conditional variables.
|
||||
if (acquire(g))
|
||||
{
|
||||
mw.unlock = false;
|
||||
return 1; // The mutex still locked.
|
||||
}
|
||||
return 0; // The mutex will be unlocked via wrapper.
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -181,8 +267,24 @@ namespace __cxxabiv1
|
||||
extern "C"
|
||||
void __cxa_guard_abort (__guard *g)
|
||||
{
|
||||
recursion_pop (g);
|
||||
#ifdef __GTHREADS
|
||||
#ifdef __GTHREAD_HAS_COND
|
||||
if (__gthread_active_p())
|
||||
{
|
||||
mutex_wrapper mw;
|
||||
|
||||
set_init_in_progress_flag(g, 0);
|
||||
|
||||
// If we abort, we still need to wake up all other threads waiting for
|
||||
// the conditional variable.
|
||||
get_static_cond().broadcast();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
set_init_in_progress_flag(g, 0);
|
||||
#if defined(__GTHREADS) && !defined(__GTHREAD_HAS_COND)
|
||||
// This provides compatibility with older systems not supporting POSIX like
|
||||
// conditional variables.
|
||||
if (__gthread_active_p ())
|
||||
static_mutex->unlock();
|
||||
#endif
|
||||
@ -191,10 +293,26 @@ namespace __cxxabiv1
|
||||
extern "C"
|
||||
void __cxa_guard_release (__guard *g)
|
||||
{
|
||||
recursion_pop (g);
|
||||
#ifdef __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);
|
||||
#ifdef __GTHREADS
|
||||
if (__gthread_active_p ())
|
||||
|
||||
#if defined(__GTHREADS) && !defined(__GTHREAD_HAS_COND)
|
||||
// This provides compatibility with older systems not supporting POSIX like
|
||||
// conditional variables.
|
||||
if (__gthread_active_p())
|
||||
static_mutex->unlock();
|
||||
#endif
|
||||
}
|
||||
|
67
libstdc++-v3/testsuite/thread/guard.cc
Normal file
67
libstdc++-v3/testsuite/thread/guard.cc
Normal file
@ -0,0 +1,67 @@
|
||||
//
|
||||
// Copyright (C) 2007 Free Software Foundation, Inc.
|
||||
//
|
||||
// This file is part of the GNU ISO C++ Library. This library is free
|
||||
// software; you can redistribute it and/or modify it under the
|
||||
// terms of the GNU General Public License as published by the
|
||||
// Free Software Foundation; either version 2, or (at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this library; see the file COPYING. If not, write to the Free
|
||||
// Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
// USA.
|
||||
|
||||
// { dg-do run { target *-*-freebsd* *-*-netbsd* *-*-linux* *-*-solaris* *-*-cygwin *-*-darwin* alpha*-*-osf* } }
|
||||
// { dg-options "-pthread" { target *-*-freebsd* *-*-netbsd* *-*-linux* *-*-solaris* *-*-darwin* alpha*-*-osf* } }
|
||||
|
||||
#include <cstdlib>
|
||||
#include <pthread.h>
|
||||
|
||||
// This used to deadlock with the old libstdc++ because there is only one
|
||||
// global mutex guarding initialization of statics and it is held during by
|
||||
// the initializer thread of a static until the variable is completely
|
||||
// initialized. If the initializer thread creates and waits for another thread
|
||||
// which also initializes a static variable, there will be a deadlock because
|
||||
// the first thread is holding the mutex and waiting for the second thread,
|
||||
// which is blocked when it is acquiring the mutex.
|
||||
|
||||
int
|
||||
get_bar (void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void*
|
||||
do_something (void *arg)
|
||||
{
|
||||
static int bar = get_bar ();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
get_foo (void)
|
||||
{
|
||||
int status;
|
||||
pthread_t new_thread;
|
||||
|
||||
if (pthread_create (&new_thread, NULL, do_something, NULL) != 0)
|
||||
std::abort ();
|
||||
|
||||
if (pthread_join (new_thread, NULL) != 0)
|
||||
std::abort ();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
static int foo = get_foo ();
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user