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/archive.h
|
||||||
wx/arrimpl.cpp
|
wx/arrimpl.cpp
|
||||||
wx/arrstr.h
|
wx/arrstr.h
|
||||||
|
wx/atomic.h
|
||||||
wx/beforestd.h
|
wx/beforestd.h
|
||||||
wx/buffer.h
|
wx/buffer.h
|
||||||
wx/build.h
|
wx/build.h
|
||||||
|
@@ -106,6 +106,7 @@ All:
|
|||||||
- Added wxStreamBuffer::Truncate() (Stas Sergeev)
|
- Added wxStreamBuffer::Truncate() (Stas Sergeev)
|
||||||
- Allow using wxEventLoop in console applications (Lukasz Michalski)
|
- Allow using wxEventLoop in console applications (Lukasz Michalski)
|
||||||
- Added functions for Base64 en/decoding (Charles Reimers)
|
- 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
|
- wxLogInterposer has been added to replace wxLogPassThrough and new
|
||||||
wxLogInterposerTemp was added
|
wxLogInterposerTemp was added
|
||||||
|
|
||||||
|
@@ -35,6 +35,8 @@ the corresponding topic.
|
|||||||
\helpref{wxASSERT}{wxassert}\\
|
\helpref{wxASSERT}{wxassert}\\
|
||||||
\helpref{wxASSERT\_MIN\_BITSIZE}{wxassertminbitsize}\\
|
\helpref{wxASSERT\_MIN\_BITSIZE}{wxassertminbitsize}\\
|
||||||
\helpref{wxASSERT\_MSG}{wxassertmsg}\\
|
\helpref{wxASSERT\_MSG}{wxassertmsg}\\
|
||||||
|
\helpref{wxAtomicDec}{wxatomicdec}\\
|
||||||
|
\helpref{wxAtomicInc}{wxatomicinc}\\
|
||||||
\helpref{wxBeginBusyCursor}{wxbeginbusycursor}\\
|
\helpref{wxBeginBusyCursor}{wxbeginbusycursor}\\
|
||||||
\helpref{wxBell}{wxbell}\\
|
\helpref{wxBell}{wxbell}\\
|
||||||
\helpref{wxBITMAP}{wxbitmapmacro}\\
|
\helpref{wxBITMAP}{wxbitmapmacro}\\
|
||||||
@@ -4721,3 +4723,42 @@ Returns \true on success.
|
|||||||
\wxheading{See also}
|
\wxheading{See also}
|
||||||
|
|
||||||
\helpref{wxSetEnv}{wxsetenv}
|
\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/textstreamtest.cpp
|
||||||
streams/zlibstream.cpp
|
streams/zlibstream.cpp
|
||||||
textfile/textfiletest.cpp
|
textfile/textfiletest.cpp
|
||||||
|
thread/atomic.cpp
|
||||||
uris/uris.cpp
|
uris/uris.cpp
|
||||||
</sources>
|
</sources>
|
||||||
<wx-lib>net</wx-lib>
|
<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