-// Copyright (C) 2002, 2004, 2006, 2008, 2009, 2010, 2011
-// Free Software Foundation, Inc.
+// Copyright (C) 2002-2024 Free Software Foundation, Inc.
//
// This file is part of GCC.
//
#include <cxxabi.h>
#include <exception>
#include <new>
+
+#ifdef __USING_MCFGTHREAD__
+
+#include <mcfgthread/cxa.h>
+
+namespace __cxxabiv1 {
+
+extern "C" int
+__cxa_guard_acquire (__guard* g) _GLIBCXX_NOTHROW
+ {
+ return __MCF_cxa_guard_acquire(g);
+ }
+
+extern "C" void
+__cxa_guard_release (__guard* g) _GLIBCXX_NOTHROW
+ {
+ __MCF_cxa_guard_release(g);
+ }
+
+extern "C" void
+__cxa_guard_abort (__guard* g) _GLIBCXX_NOTHROW
+ {
+ __MCF_cxa_guard_abort(g);
+ }
+
+} // namespace __cxxabiv1
+
+#else // __USING_MCFGTHREAD__
+
#include <ext/atomicity.h>
#include <ext/concurrence.h>
+#include <bits/atomic_lockfree_defines.h>
#if defined(__GTHREADS) && defined(__GTHREAD_HAS_COND) \
- && defined(_GLIBCXX_ATOMIC_BUILTINS_4) && defined(_GLIBCXX_HAVE_LINUX_FUTEX)
+ && (ATOMIC_INT_LOCK_FREE > 1) && defined(_GLIBCXX_HAVE_LINUX_FUTEX)
# include <climits>
# include <syscall.h>
+# include <unistd.h>
# define _GLIBCXX_USE_FUTEX
# define _GLIBCXX_FUTEX_WAIT 0
# define _GLIBCXX_FUTEX_WAKE 1
# if defined(__GTHREAD_HAS_COND) && !defined(_GLIBCXX_USE_FUTEX)
namespace
{
- // A single conditional variable controlling all static initializations.
+ // A single condition variable controlling all static initializations.
static __gnu_cxx::__cond* static_cond;
// using a fake type to avoid initializing a static class.
# endif
# ifndef _GLIBCXX_GUARD_TEST_AND_ACQUIRE
+
+// Test the guard variable with a memory load with
+// acquire semantics.
+
inline bool
__test_and_acquire (__cxxabiv1::__guard *g)
{
- bool b = _GLIBCXX_GUARD_TEST (g);
- _GLIBCXX_READ_MEM_BARRIER;
- return b;
+ unsigned char __c;
+ unsigned char *__p = reinterpret_cast<unsigned char *>(g);
+ __atomic_load (__p, &__c, __ATOMIC_ACQUIRE);
+ (void) __p;
+ return _GLIBCXX_GUARD_TEST(&__c);
}
# define _GLIBCXX_GUARD_TEST_AND_ACQUIRE(G) __test_and_acquire (G)
# endif
# ifndef _GLIBCXX_GUARD_SET_AND_RELEASE
+
+// Set the guard variable to 1 with memory order release semantics.
+
inline void
__set_and_release (__cxxabiv1::__guard *g)
{
- _GLIBCXX_WRITE_MEM_BARRIER;
- _GLIBCXX_GUARD_SET (g);
+ unsigned char *__p = reinterpret_cast<unsigned char *>(g);
+ unsigned char val = 1;
+ __atomic_store (__p, &val, __ATOMIC_RELEASE);
+ (void) __p;
}
# define _GLIBCXX_GUARD_SET_AND_RELEASE(G) __set_and_release (G)
# endif
#endif /* __GTHREADS */
//
-// Here are C++ run-time routines for guarded initiailization of static
+// Here are C++ run-time routines for guarded initialization of static
// variables. There are 4 scenarios under which these routines are called:
//
// 1. Threads not supported (__GTHREADS not defined)
//
// 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.
+// initialization 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 attempts to acquire 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
+// The new code here can handle this situation and thus is more robust. However,
+// we need to use the POSIX thread condition 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
+// like condition variables. For platforms that do not support condition
// variables, we need to fall back to the old code.
-// If _GLIBCXX_USE_FUTEX, no global mutex or conditional variable is used,
+// If _GLIBCXX_USE_FUTEX, no global mutex or condition variable is used,
// only atomic operations are used together with futex syscall.
// Valid values of the first integer in guard are:
// 0 No thread encountered the guarded init
static inline void
throw_recursive_init_exception()
{
-#ifdef __EXCEPTIONS
+#if __cpp_exceptions
throw __gnu_cxx::recursive_init_error();
#else
// Use __builtin_trap so we don't require abort().
#endif
}
- // acuire() is a helper function used to acquire guard if thread support is
+ // acquire() 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)
return 0;
# ifdef _GLIBCXX_USE_FUTEX
- // If __sync_* and futex syscall are supported, don't use any global
+ // If __atomic_* and futex syscall are supported, don't use any global
// mutex.
- if (__gthread_active_p ())
+
+ // Use the same bits in the guard variable whether single-threaded or not,
+ // so that __cxa_guard_release and __cxa_guard_abort match the logic here
+ // even if __libc_single_threaded becomes false between now and then.
+
+ if (__gnu_cxx::__is_single_threaded())
+ {
+ // No need to use atomics, and no need to wait for other threads.
+ int *gi = (int *) (void *) g;
+ if (*gi == 0)
+ {
+ *gi = _GLIBCXX_GUARD_PENDING_BIT;
+ return 1;
+ }
+ else
+ throw_recursive_init_exception();
+ }
+ else
{
int *gi = (int *) (void *) g;
const int guard_bit = _GLIBCXX_GUARD_BIT;
while (1)
{
- int old = __sync_val_compare_and_swap (gi, 0, pending_bit);
- if (old == 0)
- return 1; // This thread should do the initialization.
-
- if (old == guard_bit)
- return 0; // Already initialized.
-
- if (old == pending_bit)
+ int expected(0);
+ if (__atomic_compare_exchange_n(gi, &expected, pending_bit, false,
+ __ATOMIC_ACQ_REL,
+ __ATOMIC_ACQUIRE))
{
- int newv = old | waiting_bit;
- if (__sync_val_compare_and_swap (gi, old, newv) != old)
- continue;
-
- old = newv;
+ // This thread should do the initialization.
+ return 1;
+ }
+
+ if (expected == guard_bit)
+ {
+ // Already initialized.
+ return 0;
}
- syscall (SYS_futex, gi, _GLIBCXX_FUTEX_WAIT, old, 0);
+ if (expected == pending_bit)
+ {
+ // Use acquire here.
+ int newv = expected | waiting_bit;
+ if (!__atomic_compare_exchange_n(gi, &expected, newv, false,
+ __ATOMIC_ACQ_REL,
+ __ATOMIC_ACQUIRE))
+ {
+ if (expected == guard_bit)
+ {
+ // Make a thread that failed to set the
+ // waiting bit exit the function earlier,
+ // if it detects that another thread has
+ // successfully finished initialising.
+ return 0;
+ }
+ if (expected == 0)
+ continue;
+ }
+
+ expected = newv;
+ }
+
+ syscall (SYS_futex, gi, _GLIBCXX_FUTEX_WAIT, expected, 0);
}
}
-# else
+# else // ! _GLIBCXX_USE_FUTEX
if (__gthread_active_p ())
{
mutex_wrapper mw;
{
// 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
+ // condition variable. We will lock the mutex again after
// this.
get_static_cond().wait_recursive(&get_static_mutex());
}
}
# else
// This provides compatibility with older systems not supporting
- // POSIX like conditional variables.
+ // POSIX like condition variables.
if (acquire(g))
{
mw.unlock = false;
}
}
# endif
-#endif
+#endif // ! __GTHREADS
return acquire (g);
}
extern "C"
- void __cxa_guard_abort (__guard *g) throw ()
+ void __cxa_guard_abort (__guard *g) noexcept
{
#ifdef _GLIBCXX_USE_FUTEX
- // If __sync_* and futex syscall are supported, don't use any global
+ // If __atomic_* and futex syscall are supported, don't use any global
// mutex.
- if (__gthread_active_p ())
+
+ if (__gnu_cxx::__is_single_threaded())
+ {
+ // No need to use atomics, and no other threads to wake.
+ int *gi = (int *) (void *) g;
+ *gi = 0;
+ return;
+ }
+ else
{
int *gi = (int *) (void *) g;
const int waiting_bit = _GLIBCXX_GUARD_WAITING_BIT;
- int old = __sync_lock_test_and_set (gi, 0);
+ int old = __atomic_exchange_n (gi, 0, __ATOMIC_ACQ_REL);
if ((old & waiting_bit) != 0)
syscall (SYS_futex, gi, _GLIBCXX_FUTEX_WAKE, INT_MAX);
set_init_in_progress_flag(g, 0);
// If we abort, we still need to wake up all other threads waiting for
- // the conditional variable.
+ // the condition variable.
get_static_cond().broadcast();
return;
}
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.
+ // condition variables.
if (__gthread_active_p ())
static_mutex->unlock();
#endif
}
extern "C"
- void __cxa_guard_release (__guard *g) throw ()
+ void __cxa_guard_release (__guard *g) noexcept
{
#ifdef _GLIBCXX_USE_FUTEX
- // If __sync_* and futex syscall are supported, don't use any global
+ // If __atomic_* and futex syscall are supported, don't use any global
// mutex.
- if (__gthread_active_p ())
+
+ if (__gnu_cxx::__is_single_threaded())
+ {
+ int *gi = (int *) (void *) g;
+ *gi = _GLIBCXX_GUARD_BIT;
+ return;
+ }
+ else
{
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);
+ int old = __atomic_exchange_n (gi, guard_bit, __ATOMIC_ACQ_REL);
if ((old & waiting_bit) != 0)
syscall (SYS_futex, gi, _GLIBCXX_FUTEX_WAKE, INT_MAX);
return;
}
+
#elif defined(__GTHREAD_HAS_COND)
if (__gthread_active_p())
{
#if defined(__GTHREADS) && !defined(__GTHREAD_HAS_COND)
// This provides compatibility with older systems not supporting POSIX like
- // conditional variables.
+ // condition variables.
if (__gthread_active_p())
static_mutex->unlock();
#endif
}
}
+
+#endif // __USING_MCFGTHREAD__