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])
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_TRY_LINK(
[],
[
unsigned int value=0;
/* wxAtomicInc doesn't use return value here */
__sync_fetch_and_add(&value, 2);
__sync_sub_and_fetch(&value, 1);
/* but wxAtomicDec does, so mimic that: */
volatile unsigned int r1 = __sync_add_and_fetch(&value, 2);
volatile unsigned int r2 = __sync_sub_and_fetch(&value, 1);
],
wx_cv_cc_gcc_atomic_builtins=yes,

9
configure vendored
View File

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

View File

@@ -17,10 +17,7 @@
// get the value of wxUSE_THREADS configuration flag
#include "wx/defs.h"
// constraints on the various functions:
// - 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).
// these functions return the new value, after the operation
#if wxUSE_THREADS
@@ -31,9 +28,9 @@
// http://bugs.mysql.com/bug.php?id=28456
// 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)
@@ -47,9 +44,9 @@ inline wxUint32 wxAtomicDec (wxUint32 &value)
// include standard Windows headers
#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)
@@ -60,9 +57,9 @@ inline wxUint32 wxAtomicDec (wxUint32 &value)
#elif defined(__DARWIN__)
#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)
@@ -76,7 +73,7 @@ inline wxUint32 wxAtomicDec (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)
@@ -94,7 +91,7 @@ inline wxUint32 wxAtomicDec (wxUint32 &value)
#else // else of wxUSE_THREADS
// 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; }
#endif // !wxUSE_THREADS
@@ -120,10 +117,10 @@ public:
wxAtomicInt32& operator=(wxInt32 v) { m_value = v; return *this; }
void Inc()
wxInt32 Inc()
{
wxCriticalSectionLocker lock(m_locker);
++m_value;
return ++m_value;
}
wxInt32 Dec()
@@ -137,14 +134,14 @@ private:
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(); }
#else // !wxNEEDS_GENERIC_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); }
typedef wxInt32 wxAtomicInt32;

View File

@@ -16,21 +16,27 @@
/**
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,
implementation of this function. If such implementation is available, the
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
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}
*/
void wxAtomicInc(wxAtomicInt& value);
wxInt32 wxAtomicInc(wxAtomicInt& value);
/**
This function decrements value in an atomic manner.
Returns 0 if value is 0 after decrement or any non-zero value (not
necessarily equal to the value of the variable) otherwise.
Returns the new value after decrementing it.
@see wxAtomicInc

View File

@@ -28,7 +28,7 @@
#endif
#include "wx/init.h"
#include "wx/thread.h"
#include "wx/atomic.h"
#include "wx/scopedptr.h"
#include "wx/except.h"
@@ -130,21 +130,20 @@ static inline void Use(void *) { }
static struct InitData
{
InitData()
: nInitCount(0)
{
nInitCount = 0;
#if wxUSE_UNICODE
argc = argcOrig = 0;
// argv = argvOrig = NULL; -- not even really needed
#endif // wxUSE_UNICODE
}
// critical section protecting this struct
wxCRIT_SECT_DECLARE_MEMBER(csInit);
// number of times wxInitialize() was called minus the number of times
// 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
int argc;
@@ -530,9 +529,7 @@ bool wxInitialize()
bool wxInitialize(int& argc, wxChar **argv)
{
wxCRIT_SECT_LOCKER(lockInit, gs_initData.csInit);
if ( gs_initData.nInitCount++ )
if ( wxAtomicInc(gs_initData.nInitCount) != 1 )
{
// already initialized
return true;
@@ -544,9 +541,7 @@ bool wxInitialize(int& argc, wxChar **argv)
#if wxUSE_UNICODE
bool wxInitialize(int& argc, char **argv)
{
wxCRIT_SECT_LOCKER(lockInit, gs_initData.csInit);
if ( gs_initData.nInitCount++ )
if ( wxAtomicInc(gs_initData.nInitCount) != 1 )
{
// already initialized
return true;
@@ -558,10 +553,8 @@ bool wxInitialize(int& argc, char **argv)
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/dynarray.h"
#include "wx/log.h"
#include "wx/vector.h"
WX_DEFINE_ARRAY_PTR(wxThread *, wxArrayThread);
typedef wxVector<wxThread*> wxArrayThread;
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
// number of times to run the loops: the code takes too long to run if we use
// 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
static const wxInt32 ITERATIONS_NUM = 10000;
// ----------------------------------------------------------------------------
// test class
// test helper thread
// ----------------------------------------------------------------------------
class AtomicTestCase : public CppUnit::TestCase
namespace
{
public:
AtomicTestCase() { }
enum ETestType
{
IncAndDecMixed,
@@ -51,7 +43,6 @@ public:
DecOnly
};
private:
class MyThread : public wxThread
{
public:
@@ -65,34 +56,13 @@ private:
wxAtomicInt &m_operateOn;
ETestType m_testType;
};
} // anonymous namespace
CPPUNIT_TEST_SUITE( AtomicTestCase );
CPPUNIT_TEST( TestNoThread );
CPPUNIT_TEST( TestDecReturn );
CPPUNIT_TEST( TestTwoThreadsMix );
CPPUNIT_TEST( TestTenThreadsMix );
CPPUNIT_TEST( TestTwoThreadsSeparate );
CPPUNIT_TEST( TestTenThreadsSeparate );
CPPUNIT_TEST_SUITE_END();
// ----------------------------------------------------------------------------
// the tests themselves
// ----------------------------------------------------------------------------
void TestNoThread();
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()
TEST_CASE("Atomic::NoThread", "[atomic]")
{
wxAtomicInt int1 = 0,
int2 = 0;
@@ -103,23 +73,30 @@ void AtomicTestCase::TestNoThread()
wxAtomicDec(int2);
}
CPPUNIT_ASSERT( int1 == ITERATIONS_NUM );
CPPUNIT_ASSERT( int2 == -ITERATIONS_NUM );
CHECK( int1 == ITERATIONS_NUM );
CHECK( int2 == -ITERATIONS_NUM );
}
void AtomicTestCase::TestDecReturn()
TEST_CASE("Atomic::ReturnValue", "[atomic]")
{
wxAtomicInt i(0);
wxAtomicInc(i);
wxAtomicInc(i);
CPPUNIT_ASSERT( i == 2 );
REQUIRE( wxAtomicInc(i) == 1 );
REQUIRE( wxAtomicInc(i) == 2 );
CPPUNIT_ASSERT( wxAtomicDec(i) > 0 );
CPPUNIT_ASSERT( wxAtomicDec(i) == 0 );
REQUIRE( wxAtomicDec(i) == 1 );
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;
wxArrayThread threads;
@@ -146,7 +123,7 @@ void AtomicTestCase::TestWithThreads(int count, ETestType testType)
delete thread;
}
else
threads.Add(thread);
threads.push_back(thread);
}
for ( i = 0; i < count; ++i )
@@ -158,16 +135,16 @@ void AtomicTestCase::TestWithThreads(int count, ETestType testType)
for ( i = 0; i < count; ++i )
{
// 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];
}
CPPUNIT_ASSERT( int1 == 0 );
CHECK( int1 == 0 );
}
// ----------------------------------------------------------------------------
void *AtomicTestCase::MyThread::Entry()
void *MyThread::Entry()
{
wxInt32 negativeValuesSeen = 0;
@@ -175,19 +152,17 @@ void *AtomicTestCase::MyThread::Entry()
{
switch ( m_testType )
{
case AtomicTestCase::IncAndDecMixed:
case IncAndDecMixed:
wxAtomicInc(m_operateOn);
wxAtomicDec(m_operateOn);
if (m_operateOn < 0)
if ( wxAtomicDec(m_operateOn) < 0 )
++negativeValuesSeen;
break;
case AtomicTestCase::IncOnly:
case IncOnly:
wxAtomicInc(m_operateOn);
break;
case AtomicTestCase::DecOnly:
case DecOnly:
wxAtomicDec(m_operateOn);
break;
}