Merge branch 'tsan-warnings'

Fix a harmless warning from thread sanitizer and make wxAtomicInc() more
useful.

See #22417.
This commit is contained in:
Vadim Zeitlin
2022-05-11 02:37:26 +02:00
6 changed files with 72 additions and 107 deletions

View File

@@ -6,16 +6,13 @@ AC_DEFUN([WX_ATOMIC_BUILTINS],
[ [
AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_PROG_CC])
if test -n "$GCC"; then if test -n "$GCC"; then
AC_MSG_CHECKING([for __sync_fetch_and_add and __sync_sub_and_fetch builtins]) AC_MSG_CHECKING([for __sync_xxx_and_fetch builtins])
AC_CACHE_VAL(wx_cv_cc_gcc_atomic_builtins, [ AC_CACHE_VAL(wx_cv_cc_gcc_atomic_builtins, [
AC_TRY_LINK( AC_TRY_LINK(
[], [],
[ [
unsigned int value=0; unsigned int value=0;
/* wxAtomicInc doesn't use return value here */ volatile unsigned int r1 = __sync_add_and_fetch(&value, 2);
__sync_fetch_and_add(&value, 2);
__sync_sub_and_fetch(&value, 1);
/* but wxAtomicDec does, so mimic that: */
volatile unsigned int r2 = __sync_sub_and_fetch(&value, 1); volatile unsigned int r2 = __sync_sub_and_fetch(&value, 1);
], ],
wx_cv_cc_gcc_atomic_builtins=yes, wx_cv_cc_gcc_atomic_builtins=yes,

9
configure vendored
View File

@@ -25176,8 +25176,8 @@ fi
if test -n "$GCC"; then if test -n "$GCC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_fetch_and_add and __sync_sub_and_fetch builtins" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __sync_xxx_and_fetch builtins" >&5
$as_echo_n "checking for __sync_fetch_and_add and __sync_sub_and_fetch builtins... " >&6; } $as_echo_n "checking for __sync_xxx_and_fetch builtins... " >&6; }
if ${wx_cv_cc_gcc_atomic_builtins+:} false; then : if ${wx_cv_cc_gcc_atomic_builtins+:} false; then :
$as_echo_n "(cached) " >&6 $as_echo_n "(cached) " >&6
else else
@@ -25190,10 +25190,7 @@ main ()
{ {
unsigned int value=0; unsigned int value=0;
/* wxAtomicInc doesn't use return value here */ volatile unsigned int r1 = __sync_add_and_fetch(&value, 2);
__sync_fetch_and_add(&value, 2);
__sync_sub_and_fetch(&value, 1);
/* but wxAtomicDec does, so mimic that: */
volatile unsigned int r2 = __sync_sub_and_fetch(&value, 1); volatile unsigned int r2 = __sync_sub_and_fetch(&value, 1);
; ;

View File

@@ -17,10 +17,7 @@
// get the value of wxUSE_THREADS configuration flag // get the value of wxUSE_THREADS configuration flag
#include "wx/defs.h" #include "wx/defs.h"
// constraints on the various functions: // these functions return the new value, after the operation
// - wxAtomicDec must return a zero value if the value is zero once
// decremented else it must return any non-zero value (the true value is OK
// but not necessary).
#if wxUSE_THREADS #if wxUSE_THREADS
@@ -31,9 +28,9 @@
// http://bugs.mysql.com/bug.php?id=28456 // http://bugs.mysql.com/bug.php?id=28456
// http://golubenco.org/blog/atomic-operations/ // http://golubenco.org/blog/atomic-operations/
inline void wxAtomicInc (wxUint32 &value) inline wxUint32 wxAtomicInc (wxUint32 &value)
{ {
__sync_fetch_and_add(&value, 1); return __sync_add_and_fetch(&value, 1);
} }
inline wxUint32 wxAtomicDec (wxUint32 &value) inline wxUint32 wxAtomicDec (wxUint32 &value)
@@ -47,9 +44,9 @@ inline wxUint32 wxAtomicDec (wxUint32 &value)
// include standard Windows headers // include standard Windows headers
#include "wx/msw/wrapwin.h" #include "wx/msw/wrapwin.h"
inline void wxAtomicInc (wxUint32 &value) inline wxUint32 wxAtomicInc (wxUint32 &value)
{ {
InterlockedIncrement ((LONG*)&value); return InterlockedIncrement ((LONG*)&value);
} }
inline wxUint32 wxAtomicDec (wxUint32 &value) inline wxUint32 wxAtomicDec (wxUint32 &value)
@@ -60,9 +57,9 @@ inline wxUint32 wxAtomicDec (wxUint32 &value)
#elif defined(__DARWIN__) #elif defined(__DARWIN__)
#include "libkern/OSAtomic.h" #include "libkern/OSAtomic.h"
inline void wxAtomicInc (wxUint32 &value) inline wxUint32 wxAtomicInc (wxUint32 &value)
{ {
OSAtomicIncrement32 ((int32_t*)&value); return OSAtomicIncrement32 ((int32_t*)&value);
} }
inline wxUint32 wxAtomicDec (wxUint32 &value) inline wxUint32 wxAtomicDec (wxUint32 &value)
@@ -76,7 +73,7 @@ inline wxUint32 wxAtomicDec (wxUint32 &value)
inline void wxAtomicInc (wxUint32 &value) inline void wxAtomicInc (wxUint32 &value)
{ {
atomic_add_32 ((uint32_t*)&value, 1); return atomic_add_32_nv ((uint32_t*)&value, 1);
} }
inline wxUint32 wxAtomicDec (wxUint32 &value) inline wxUint32 wxAtomicDec (wxUint32 &value)
@@ -94,7 +91,7 @@ inline wxUint32 wxAtomicDec (wxUint32 &value)
#else // else of wxUSE_THREADS #else // else of wxUSE_THREADS
// if no threads are used we can safely use simple ++/-- // if no threads are used we can safely use simple ++/--
inline void wxAtomicInc (wxUint32 &value) { ++value; } inline wxUint32 wxAtomicInc (wxUint32 &value) { return ++value; }
inline wxUint32 wxAtomicDec (wxUint32 &value) { return --value; } inline wxUint32 wxAtomicDec (wxUint32 &value) { return --value; }
#endif // !wxUSE_THREADS #endif // !wxUSE_THREADS
@@ -120,10 +117,10 @@ public:
wxAtomicInt32& operator=(wxInt32 v) { m_value = v; return *this; } wxAtomicInt32& operator=(wxInt32 v) { m_value = v; return *this; }
void Inc() wxInt32 Inc()
{ {
wxCriticalSectionLocker lock(m_locker); wxCriticalSectionLocker lock(m_locker);
++m_value; return ++m_value;
} }
wxInt32 Dec() wxInt32 Dec()
@@ -137,14 +134,14 @@ private:
wxCriticalSection m_locker; wxCriticalSection m_locker;
}; };
inline void wxAtomicInc(wxAtomicInt32 &value) { value.Inc(); } inline wxInt32 wxAtomicInc(wxAtomicInt32 &value) { return value.Inc(); }
inline wxInt32 wxAtomicDec(wxAtomicInt32 &value) { return value.Dec(); } inline wxInt32 wxAtomicDec(wxAtomicInt32 &value) { return value.Dec(); }
#else // !wxNEEDS_GENERIC_ATOMIC_OPS #else // !wxNEEDS_GENERIC_ATOMIC_OPS
#define wxHAS_ATOMIC_OPS #define wxHAS_ATOMIC_OPS
inline void wxAtomicInc(wxInt32 &value) { wxAtomicInc((wxUint32&)value); } inline wxInt32 wxAtomicInc(wxInt32 &value) { return wxAtomicInc((wxUint32&)value); }
inline wxInt32 wxAtomicDec(wxInt32 &value) { return wxAtomicDec((wxUint32&)value); } inline wxInt32 wxAtomicDec(wxInt32 &value) { return wxAtomicDec((wxUint32&)value); }
typedef wxInt32 wxAtomicInt32; typedef wxInt32 wxAtomicInt32;

View File

@@ -16,21 +16,27 @@
/** /**
This function increments @a value in an atomic manner. This function increments @a value in an atomic manner.
@note It is recommended to use @c std::atomic available in C++11 and later
instead of this function in any new code.
Whenever possible wxWidgets provides an efficient, CPU-specific, Whenever possible wxWidgets provides an efficient, CPU-specific,
implementation of this function. If such implementation is available, the implementation of this function. If such implementation is available, the
symbol wxHAS_ATOMIC_OPS is defined. Otherwise this function still exists symbol wxHAS_ATOMIC_OPS is defined. Otherwise this function still exists
but is implemented in a generic way using a critical section which can be but is implemented in a generic way using a critical section which can be
prohibitively expensive for use in performance-sensitive code. prohibitively expensive for use in performance-sensitive code.
Returns the new value after the increment (the return value is only
available since wxWidgets 3.1.7, this function doesn't return anything in
previous versions of the library).
@header{wx/atomic.h} @header{wx/atomic.h}
*/ */
void wxAtomicInc(wxAtomicInt& value); wxInt32 wxAtomicInc(wxAtomicInt& value);
/** /**
This function decrements value in an atomic manner. This function decrements value in an atomic manner.
Returns 0 if value is 0 after decrement or any non-zero value (not Returns the new value after decrementing it.
necessarily equal to the value of the variable) otherwise.
@see wxAtomicInc @see wxAtomicInc

View File

@@ -28,7 +28,7 @@
#endif #endif
#include "wx/init.h" #include "wx/init.h"
#include "wx/thread.h" #include "wx/atomic.h"
#include "wx/scopedptr.h" #include "wx/scopedptr.h"
#include "wx/except.h" #include "wx/except.h"
@@ -130,21 +130,20 @@ static inline void Use(void *) { }
static struct InitData static struct InitData
{ {
InitData() InitData()
: nInitCount(0)
{ {
nInitCount = 0;
#if wxUSE_UNICODE #if wxUSE_UNICODE
argc = argcOrig = 0; argc = argcOrig = 0;
// argv = argvOrig = NULL; -- not even really needed // argv = argvOrig = NULL; -- not even really needed
#endif // wxUSE_UNICODE #endif // wxUSE_UNICODE
} }
// critical section protecting this struct
wxCRIT_SECT_DECLARE_MEMBER(csInit);
// number of times wxInitialize() was called minus the number of times // number of times wxInitialize() was called minus the number of times
// wxUninitialize() was // wxUninitialize() was
size_t nInitCount; //
// it is atomic to allow more than one thread to call wxInitialize() but
// only one of them to actually initialize the library
wxAtomicInt nInitCount;
#if wxUSE_UNICODE #if wxUSE_UNICODE
int argc; int argc;
@@ -530,9 +529,7 @@ bool wxInitialize()
bool wxInitialize(int& argc, wxChar **argv) bool wxInitialize(int& argc, wxChar **argv)
{ {
wxCRIT_SECT_LOCKER(lockInit, gs_initData.csInit); if ( wxAtomicInc(gs_initData.nInitCount) != 1 )
if ( gs_initData.nInitCount++ )
{ {
// already initialized // already initialized
return true; return true;
@@ -544,9 +541,7 @@ bool wxInitialize(int& argc, wxChar **argv)
#if wxUSE_UNICODE #if wxUSE_UNICODE
bool wxInitialize(int& argc, char **argv) bool wxInitialize(int& argc, char **argv)
{ {
wxCRIT_SECT_LOCKER(lockInit, gs_initData.csInit); if ( wxAtomicInc(gs_initData.nInitCount) != 1 )
if ( gs_initData.nInitCount++ )
{ {
// already initialized // already initialized
return true; return true;
@@ -558,10 +553,8 @@ bool wxInitialize(int& argc, char **argv)
void wxUninitialize() void wxUninitialize()
{ {
wxCRIT_SECT_LOCKER(lockInit, gs_initData.csInit); if ( wxAtomicDec(gs_initData.nInitCount) != 0 )
return;
if ( --gs_initData.nInitCount == 0 )
{
wxEntryCleanup(); wxEntryCleanup();
} }
}

View File

@@ -20,30 +20,22 @@
#include "wx/thread.h" #include "wx/thread.h"
#include "wx/dynarray.h" #include "wx/dynarray.h"
#include "wx/log.h" #include "wx/log.h"
#include "wx/vector.h"
WX_DEFINE_ARRAY_PTR(wxThread *, wxArrayThread); typedef wxVector<wxThread*> wxArrayThread;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// constants // constants
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// number of times to run the loops: the code takes too long to run if we use static const wxInt32 ITERATIONS_NUM = 10000;
// the bigger value with generic atomic operations implementation
#ifdef wxHAS_ATOMIC_OPS
static const wxInt32 ITERATIONS_NUM = 10000000;
#else
static const wxInt32 ITERATIONS_NUM = 1000;
#endif
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// test class // test helper thread
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
class AtomicTestCase : public CppUnit::TestCase namespace
{ {
public:
AtomicTestCase() { }
enum ETestType enum ETestType
{ {
IncAndDecMixed, IncAndDecMixed,
@@ -51,7 +43,6 @@ public:
DecOnly DecOnly
}; };
private:
class MyThread : public wxThread class MyThread : public wxThread
{ {
public: public:
@@ -65,34 +56,13 @@ private:
wxAtomicInt &m_operateOn; wxAtomicInt &m_operateOn;
ETestType m_testType; ETestType m_testType;
}; };
} // anonymous namespace
CPPUNIT_TEST_SUITE( AtomicTestCase ); // ----------------------------------------------------------------------------
CPPUNIT_TEST( TestNoThread ); // the tests themselves
CPPUNIT_TEST( TestDecReturn ); // ----------------------------------------------------------------------------
CPPUNIT_TEST( TestTwoThreadsMix );
CPPUNIT_TEST( TestTenThreadsMix );
CPPUNIT_TEST( TestTwoThreadsSeparate );
CPPUNIT_TEST( TestTenThreadsSeparate );
CPPUNIT_TEST_SUITE_END();
void TestNoThread(); TEST_CASE("Atomic::NoThread", "[atomic]")
void TestDecReturn();
void TestTenThreadsMix() { TestWithThreads(10, IncAndDecMixed); }
void TestTwoThreadsMix() { TestWithThreads(2, IncAndDecMixed); }
void TestTenThreadsSeparate() { TestWithThreads(10, IncOnly); }
void TestTwoThreadsSeparate() { TestWithThreads(2, IncOnly); }
void TestWithThreads(int count, ETestType testtype);
wxDECLARE_NO_COPY_CLASS(AtomicTestCase);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( AtomicTestCase );
// also include in its own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( AtomicTestCase, "AtomicTestCase" );
void AtomicTestCase::TestNoThread()
{ {
wxAtomicInt int1 = 0, wxAtomicInt int1 = 0,
int2 = 0; int2 = 0;
@@ -103,23 +73,30 @@ void AtomicTestCase::TestNoThread()
wxAtomicDec(int2); wxAtomicDec(int2);
} }
CPPUNIT_ASSERT( int1 == ITERATIONS_NUM ); CHECK( int1 == ITERATIONS_NUM );
CPPUNIT_ASSERT( int2 == -ITERATIONS_NUM ); CHECK( int2 == -ITERATIONS_NUM );
} }
void AtomicTestCase::TestDecReturn() TEST_CASE("Atomic::ReturnValue", "[atomic]")
{ {
wxAtomicInt i(0); wxAtomicInt i(0);
wxAtomicInc(i); REQUIRE( wxAtomicInc(i) == 1 );
wxAtomicInc(i); REQUIRE( wxAtomicInc(i) == 2 );
CPPUNIT_ASSERT( i == 2 );
CPPUNIT_ASSERT( wxAtomicDec(i) > 0 ); REQUIRE( wxAtomicDec(i) == 1 );
CPPUNIT_ASSERT( wxAtomicDec(i) == 0 ); REQUIRE( wxAtomicDec(i) == 0 );
} }
void AtomicTestCase::TestWithThreads(int count, ETestType testType) TEST_CASE("Atomic::WithThreads", "[atomic]")
{ {
int count;
ETestType testType;
SECTION( "2 threads using inc and dec") { count = 2; testType = IncAndDecMixed; }
SECTION("10 threads using inc and dec") { count = 10; testType = IncAndDecMixed; }
SECTION( "2 threads using inc or dec" ) { count = 2; testType = IncOnly; }
SECTION("10 threads using inc or dec" ) { count = 10; testType = IncOnly; }
wxAtomicInt int1=0; wxAtomicInt int1=0;
wxArrayThread threads; wxArrayThread threads;
@@ -146,7 +123,7 @@ void AtomicTestCase::TestWithThreads(int count, ETestType testType)
delete thread; delete thread;
} }
else else
threads.Add(thread); threads.push_back(thread);
} }
for ( i = 0; i < count; ++i ) for ( i = 0; i < count; ++i )
@@ -158,16 +135,16 @@ void AtomicTestCase::TestWithThreads(int count, ETestType testType)
for ( i = 0; i < count; ++i ) for ( i = 0; i < count; ++i )
{ {
// each thread should return 0, else it detected some problem // each thread should return 0, else it detected some problem
CPPUNIT_ASSERT (threads[i]->Wait() == (wxThread::ExitCode)0); CHECK (threads[i]->Wait() == (wxThread::ExitCode)0);
delete threads[i]; delete threads[i];
} }
CPPUNIT_ASSERT( int1 == 0 ); CHECK( int1 == 0 );
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void *AtomicTestCase::MyThread::Entry() void *MyThread::Entry()
{ {
wxInt32 negativeValuesSeen = 0; wxInt32 negativeValuesSeen = 0;
@@ -175,19 +152,17 @@ void *AtomicTestCase::MyThread::Entry()
{ {
switch ( m_testType ) switch ( m_testType )
{ {
case AtomicTestCase::IncAndDecMixed: case IncAndDecMixed:
wxAtomicInc(m_operateOn); wxAtomicInc(m_operateOn);
wxAtomicDec(m_operateOn); if ( wxAtomicDec(m_operateOn) < 0 )
if (m_operateOn < 0)
++negativeValuesSeen; ++negativeValuesSeen;
break; break;
case AtomicTestCase::IncOnly: case IncOnly:
wxAtomicInc(m_operateOn); wxAtomicInc(m_operateOn);
break; break;
case AtomicTestCase::DecOnly: case DecOnly:
wxAtomicDec(m_operateOn); wxAtomicDec(m_operateOn);
break; break;
} }