From: Vadim Zeitlin Date: Wed, 4 Jul 2007 20:54:14 +0000 (+0000) Subject: added wxAtomicInc/Dec() functions (patch 1739486) X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/cde76cf2a96d43f62ecb7e18c51f73e871a6d4bd added wxAtomicInc/Dec() functions (patch 1739486) git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@47121 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/build/bakefiles/files.bkl b/build/bakefiles/files.bkl index 2cdebdbf24..0a9b8fa252 100644 --- a/build/bakefiles/files.bkl +++ b/build/bakefiles/files.bkl @@ -394,6 +394,7 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file! wx/archive.h wx/arrimpl.cpp wx/arrstr.h + wx/atomic.h wx/beforestd.h wx/buffer.h wx/build.h diff --git a/docs/changes.txt b/docs/changes.txt index e3cbd0ff82..020280fbec 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -106,6 +106,7 @@ All: - Added wxStreamBuffer::Truncate() (Stas Sergeev) - Allow using wxEventLoop in console applications (Lukasz Michalski) - Added functions for Base64 en/decoding (Charles Reimers) +- Added functions for atomically inc/decrementing integers (Armel Asselin) - wxLogInterposer has been added to replace wxLogPassThrough and new wxLogInterposerTemp was added diff --git a/docs/latex/wx/function.tex b/docs/latex/wx/function.tex index 1e8528186b..eb7efe5e94 100644 --- a/docs/latex/wx/function.tex +++ b/docs/latex/wx/function.tex @@ -35,6 +35,8 @@ the corresponding topic. \helpref{wxASSERT}{wxassert}\\ \helpref{wxASSERT\_MIN\_BITSIZE}{wxassertminbitsize}\\ \helpref{wxASSERT\_MSG}{wxassertmsg}\\ +\helpref{wxAtomicDec}{wxatomicdec}\\ +\helpref{wxAtomicInc}{wxatomicinc}\\ \helpref{wxBeginBusyCursor}{wxbeginbusycursor}\\ \helpref{wxBell}{wxbell}\\ \helpref{wxBITMAP}{wxbitmapmacro}\\ @@ -4721,3 +4723,42 @@ Returns \true on success. \wxheading{See also} \helpref{wxSetEnv}{wxsetenv} + + +\section{Atomic operations}\label{atomicoperations} + +When using multi-threaded applications, it is often required to access or +modify memory which is shared between threads. Atomic integer and pointer +operations are an efficient way to handle this issue (another, less efficient, +way is to use a \helpref{mutex}{wxmutex} or \helpref{critical +section}{wxcriticalsection}). A native implementation exists for Windows, +Linux, Solaris and Mac OS X, for other OS, a +\helpref{wxCriticalSection}{wxcriticalsection} is used to protect the data. + +One particular application is reference counting (used by so-called smart +pointers). + +You should define your variable with the type wxAtomicInt in order to apply +atomic operations to it. + +\wxheading{Include files} + + + +\membersection{::wxAtomicInc}\label{wxatomicinc} + +\func{void}{wxAtomicInc}{\param{wxAtomicInt\& }{value}} + +This function increments \arg{value} in an atomic manner. + + +\membersection{::wxAtomicDec}\label{wxatomicdec} + +\func{wxInt32}{wxAtomicDec}{\param{wxAtomicInt\& }{value}} + +This function decrements \arg{value} in an atomic manner. + +Returns 0 if \arg{value} is 0 after decrementation or any non-zero value (not +necessarily equal to the value of the variable) otherwise. + + diff --git a/include/wx/atomic.h b/include/wx/atomic.h new file mode 100644 index 0000000000..9918f63ecc --- /dev/null +++ b/include/wx/atomic.h @@ -0,0 +1,156 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wx/atomic.h +// Purpose: functions to manipulate atomically integers and pointers +// Author: Armel Asselin +// Created: 12/13/2006 +// RCS-ID: $Id$ +// Copyright: (c) Armel Asselin +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_ATOMIC_H_ +#define _WX_ATOMIC_H_ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +// 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). + +#if wxUSE_THREADS + +#if defined(__WXMSW__) + +// include standard Windows headers +#include "wx/msw/wrapwin.h" + +inline void wxAtomicInc (wxUint32 &value) +{ + InterlockedIncrement ((LONG*)&value); +} + +inline wxUint32 wxAtomicDec (wxUint32 &value) +{ + return InterlockedDecrement ((LONG*)&value); +} + +#elif defined(__WXMAC__) || defined(__DARWIN__) + +#include "libkern/OSAtomic.h" +inline void wxAtomicInc (wxUint32 &value) +{ + OSAtomicIncrement32 ((int32_t*)&value); +} + +inline wxUint32 wxAtomicDec (wxUint32 &value) +{ + return OSAtomicDecrement32 ((int32_t*)&value); +} + +#elif defined(__LINUX__) + +#include + +inline void wxAtomicInc (wxUint32 &value) +{ + atomic_inc ((atomic_t*)&value); +} + +inline wxUint32 wxAtomicDec (wxUint32 &value) +{ + return atomic_dec_and_test ((atomic_t*)&value) ? 0 : 1; +} + +#elif defined (__SOLARIS__) + +#include + +inline void wxAtomicInc (wxUint32 &value) +{ + atomic_add_32 ((uint32_t*)&value, 1); +} + +inline wxUint32 wxAtomicDec (wxUint32 &value) +{ + return atomic_add_32_nv ((uint32_t*)&value, (uint32_t)-1); +} + +#else // unknown platform + +// it will result in inclusion if the generic implementation code a bit later in this page +#define wxHAS_GENERIC_ATOMIC_OPS 1 + +#endif // unknown platform + +#else // else of wxUSE_THREADS +// if no threads are used we can safely use simple ++/-- + +inline void wxAtomicInc (wxUint32 &value) { ++value; } +inline wxUint32 wxAtomicDec (wxUint32 &value) { return --value; } + +#endif // !wxUSE_THREADS + +// ---------------------------------------------------------------------------- +// proxies to actual implementations, but for various other types with same +// behaviour +// ---------------------------------------------------------------------------- + +#if !defined(wxHAS_GENERIC_ATOMIC_OPS) +#define wxHAS_GENERIC_ATOMIC_OPS 0 +#endif + +#if wxHAS_GENERIC_ATOMIC_OPS +#include "wx/thread.h" // for wxCriticalSection + +class wxAtomicInt32 +{ +public: + wxAtomicInt() { } // non initialized for consistency with basic int type + wxAtomicInt(wxInt32 v) : m_value(v) { } + + operator wxInt32() const { return m_value; } + operator wxInt32&() { return m_value; } + + wxAtomicInt& operator=(wxInt32 v) { m_value = v; return *this; } + + void Inc() + { + wxCriticalSectionLocker lock(m_locker); + ++m_value; + } + + wxInt32 Dec() + { + wxCriticalSectionLocker lock(m_locker); + return --m_value; + } + +private: + volatile wxInt32 m_value; + wxCriticalSection m_locker; +}; + +inline void wxAtomicInc(wxAtomicInt &value) { value.Inc(); } +inline wxInt32 wxAtomicDec(wxAtomicInt &value) { return value.Dec(); } + +#else // !wxHAS_GENERIC_ATOMIC_OPS + +inline void wxAtomicInc(wxInt32 &value) { wxAtomicInc((wxUint32&)value); } +inline wxInt32 wxAtomicDec(wxInt32 &value) { return wxAtomicDec((wxUint32&)value); } + +typedef wxInt32 wxAtomicInt32; + +#endif // wxHAS_GENERIC_ATOMIC_OPS + +// all the native implementations use 32 bits currently +// for a 64 bits implementation we could use (a future) wxAtomicInt64 as +// default type +typedef wxAtomicInt32 wxAtomicInt; + +#endif // _WX_ATOMIC_H_ diff --git a/tests/test.bkl b/tests/test.bkl index 3a0c85f94b..925a6556dc 100644 --- a/tests/test.bkl +++ b/tests/test.bkl @@ -64,6 +64,7 @@ streams/textstreamtest.cpp streams/zlibstream.cpp textfile/textfiletest.cpp + thread/atomic.cpp uris/uris.cpp net diff --git a/tests/thread/atomic.cpp b/tests/thread/atomic.cpp new file mode 100644 index 0000000000..d49019c6ab --- /dev/null +++ b/tests/thread/atomic.cpp @@ -0,0 +1,172 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: tests/thread/atomic.cpp +// Purpose: wxAtomic??? unit test +// Author: Armel Asselin +// Created: 2006-12-14 +// RCS-ID: $Id$ +// Copyright: (c) 2006 Armel Asselin +/////////////////////////////////////////////////////////////////////////////// + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +#include "testprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#endif // WX_PRECOMP + +#include "wx/atomic.h" +#include "wx/thread.h" +#include "wx/dynarray.h" +WX_DEFINE_ARRAY_PTR(wxThread *, wxArrayThread); + +// ---------------------------------------------------------------------------- +// test class +// ---------------------------------------------------------------------------- + +class AtomicTestCase : public CppUnit::TestCase +{ +public: + AtomicTestCase() { } + +private: + + enum ETestType + { + IncAndDecMixed, + IncOnly, + DecOnly + }; + + class MyThread : public wxThread + { + public: + MyThread(wxAtomicInt &operateOn, ETestType testType) : wxThread(wxTHREAD_JOINABLE), + m_operateOn(operateOn), m_testType(testType) {} + + // thread execution starts here + virtual void *Entry(); + + public: + wxAtomicInt &m_operateOn; + ETestType m_testType; + }; + + CPPUNIT_TEST_SUITE( AtomicTestCase ); + CPPUNIT_TEST( TestNoThread ); + CPPUNIT_TEST( TestTwoThreadsMix ); + CPPUNIT_TEST( TestTenThreadsMix ); + CPPUNIT_TEST( TestTwoThreadsSeparate ); + CPPUNIT_TEST( TestTenThreadsSeparate ); + CPPUNIT_TEST_SUITE_END(); + + void TestNoThread(); + 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); + + DECLARE_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 it's own registry so that these tests can be run alone +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( AtomicTestCase, "AtomicTestCase" ); + +void AtomicTestCase::TestNoThread() +{ + wxAtomicInt int1=0, int2=0; + + for (wxInt32 i=0; i<10000000; ++i) + { + wxAtomicInc(int1); + wxAtomicDec(int2); + } + + CPPUNIT_ASSERT( int1 == 10000000 ); + CPPUNIT_ASSERT( int2 == -10000000 ); +} + +void AtomicTestCase::TestWithThreads(int count, ETestType testType) +{ + wxAtomicInt int1=0; + + wxArrayThread threads; + + int i; + for ( i = 0; i < count; ++i ) + { + ETestType actualThreadType; + switch(testType) + { + default: + actualThreadType = testType; + break; + case IncOnly: + actualThreadType = (i&1)==0 ? IncOnly : DecOnly; + break; + } + + MyThread *thread = new MyThread(int1, actualThreadType); + + if ( thread->Create() != wxTHREAD_NO_ERROR ) + { + wxLogError(wxT("Can't create thread!")); + } + else + threads.Add(thread); + } + + for ( i = 0; i < count; ++i ) + { + threads[i]->Run(); + } + + + for ( i = 0; i < count; ++i ) + { + // each thread should return 0, else it detected some problem + CPPUNIT_ASSERT (threads[i]->Wait() == (wxThread::ExitCode)0); + } + + CPPUNIT_ASSERT( int1 == 0 ); +} + +// ---------------------------------------------------------------------------- + +void *AtomicTestCase::MyThread::Entry() +{ + wxInt32 negativeValuesSeen = 0; + + for (wxInt32 i=0; i<10000000; ++i) + { + switch (m_testType) + { + case AtomicTestCase::IncAndDecMixed: + wxAtomicInc(m_operateOn); + wxAtomicDec(m_operateOn); + + if (m_operateOn < 0) + ++negativeValuesSeen; + break; + + case AtomicTestCase::IncOnly: + wxAtomicInc(m_operateOn); + break; + + case AtomicTestCase::DecOnly: + wxAtomicDec(m_operateOn); + break; + } + } + + return (wxThread::ExitCode)negativeValuesSeen; +}