wx/archive.h
wx/arrimpl.cpp
wx/arrstr.h
+ wx/atomic.h
wx/beforestd.h
wx/buffer.h
wx/build.h
- 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
\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}\\
\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.
+
+
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// 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_
streams/textstreamtest.cpp
streams/zlibstream.cpp
textfile/textfiletest.cpp
+ thread/atomic.cpp
uris/uris.cpp
</sources>
<wx-lib>net</wx-lib>
--- /dev/null
+///////////////////////////////////////////////////////////////////////////////
+// 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;
+}