]> git.saurik.com Git - wxWidgets.git/commitdiff
added wxAtomicInc/Dec() functions (patch 1739486)
authorVadim Zeitlin <vadim@wxwidgets.org>
Wed, 4 Jul 2007 20:54:14 +0000 (20:54 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Wed, 4 Jul 2007 20:54:14 +0000 (20:54 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@47121 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

build/bakefiles/files.bkl
docs/changes.txt
docs/latex/wx/function.tex
include/wx/atomic.h [new file with mode: 0644]
tests/test.bkl
tests/thread/atomic.cpp [new file with mode: 0644]

index 2cdebdbf240d46452d6cbf94f8f5fb3fbbf29f5a..0a9b8fa252212ae2ed08a13b44ded375d9c1d923 100644 (file)
@@ -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
index e3cbd0ff8216ceba55370145e5b31e7a5dabc961..020280fbec77799adf79bfd294dc3d766dfb46bd 100644 (file)
@@ -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
 
index 1e8528186b70c881531cfdf7cbd26cae865bef07..eb7efe5e94067bf6f35cd38e026596463cd2721f 100644 (file)
@@ -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}
+
+<wx/atomic.h>
+
+\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 (file)
index 0000000..9918f63
--- /dev/null
@@ -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 <asm/atomic.h>
+
+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 <atomic.h>
+
+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_
index 3a0c85f94b98159827800d58bdf685b80579d02f..925a6556dce2ac9418621ea594fa3080bdaef332 100644 (file)
@@ -64,6 +64,7 @@
             streams/textstreamtest.cpp
             streams/zlibstream.cpp
             textfile/textfiletest.cpp
+            thread/atomic.cpp
             uris/uris.cpp
         </sources>
         <wx-lib>net</wx-lib>
diff --git a/tests/thread/atomic.cpp b/tests/thread/atomic.cpp
new file mode 100644 (file)
index 0000000..d49019c
--- /dev/null
@@ -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;
+}