added wxAtomicInc/Dec() functions (patch 1739486)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@47121 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2007-07-04 20:54:14 +00:00
parent 2b753fb4c2
commit cde76cf2a9
6 changed files with 372 additions and 0 deletions

View 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

View 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

View 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.

156
include/wx/atomic.h Normal file
View File

@@ -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_

View 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>

172
tests/thread/atomic.cpp Normal file
View File

@@ -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;
}