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:
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
156
include/wx/atomic.h
Normal 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_
|
@@ -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
172
tests/thread/atomic.cpp
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user