chanegd wxTlsValue to be pointer-like instead of value-like which doesn't work for UDTs; use __thread keyword with mingw32 >= 4.3 too; use library-based thread-specific variables support in wxString cache now that it is fixed to work there; finally added a unit test for TLS stuff

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@55361 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2008-08-29 23:28:42 +00:00
parent b18f47d002
commit 8b73c5318c
17 changed files with 320 additions and 83 deletions

View File

@@ -11,9 +11,6 @@
#ifndef _WX_MSW_TLS_H_
#define _WX_MSW_TLS_H_
#include "wx/log.h"
#include "wx/intl.h"
#include "wx/msw/wrapwin.h"
// ----------------------------------------------------------------------------
@@ -27,8 +24,6 @@ public:
wxTlsKey()
{
m_slot = ::TlsAlloc();
if ( m_slot == TLS_OUT_OF_INDEXES )
wxLogError("Creating TLS key failed");
}
// return true if the key was successfully allocated
@@ -43,25 +38,14 @@ public:
// change the key value, return true if ok
bool Set(void *value)
{
if ( !::TlsSetValue(m_slot, value) )
{
wxLogSysError(_("Failed to set TLS value"));
return false;
}
return true;
return ::TlsSetValue(m_slot, value) != 0;
}
// free the key
~wxTlsKey()
{
if ( IsOk() )
{
if ( !::TlsFree(m_slot) )
{
wxLogDebug("TlsFree() failed: %08x", ::GetLastError());
}
}
::TlsFree(m_slot);
}
private:

View File

@@ -75,12 +75,6 @@
#define wxUSE_STRING_POS_CACHE 0
#endif
#ifndef wxHAS_COMPILER_TLS
// FIXME: currently the code only works with compiler TLS support
#undef wxUSE_STRING_POS_CACHE
#define wxUSE_STRING_POS_CACHE 0
#endif
#if wxUSE_STRING_POS_CACHE
#include "wx/tls.h"
@@ -600,7 +594,26 @@ private:
unsigned lastUsed;
};
static wxTLS_TYPE(Cache) ms_cache;
// notice that we must use an accessor function and not a static variable
// because when the TLS variables support is implemented in the library (and
// not by the compiler), the global s_cache variable could be not yet
// initialized when a ctor of another global object is executed and if that
// ctor uses any wxString methods, bad things happen
//
// also note that for the same reason this function _is_ MT-safe: we know
// it's going to be called during the program startup (currently during
// globals initialization but even if they ever stop using wxString, it would
// still be called by wxInitialize()), i.e. before any threads are created
static Cache& GetCache()
{
static wxTLS_TYPE(Cache) s_cache;
return wxTLS_VALUE(s_cache);
}
static Cache::Element *GetCacheBegin() { return GetCache().cached; }
static Cache::Element *GetCacheEnd() { return GetCacheBegin() + Cache::SIZE; }
static unsigned& LastUsedCacheElement() { return GetCache().lastUsed; }
friend struct wxStrCacheDumper;
@@ -644,9 +657,7 @@ private:
// profiling seems to show a small but consistent gain if we use this
// simple loop instead of starting from the last used element (there are
// a lot of misses in this function...)
for ( Cache::Element *c = ms_cache.cached;
c != ms_cache.cached + Cache::SIZE;
c++ )
for ( Cache::Element *c = GetCacheBegin(); c != GetCacheEnd(); c++ )
{
if ( c->str == this )
return c;
@@ -660,9 +671,9 @@ private:
// its corresponding index in the byte string or not
Cache::Element *GetCacheElement() const
{
Cache::Element * const cacheBegin = ms_cache.cached;
Cache::Element * const cacheEnd = ms_cache.cached + Cache::SIZE;
Cache::Element * const cacheStart = cacheBegin + ms_cache.lastUsed;
Cache::Element * const cacheBegin = GetCacheBegin();
Cache::Element * const cacheEnd = GetCacheEnd();
Cache::Element * const cacheStart = cacheBegin + LastUsedCacheElement();
// check the last used first, this does no (measurable) harm for a miss
// but does help for simple loops addressing the same string all the time
@@ -685,7 +696,7 @@ private:
c->Reset();
// and remember the last used element
ms_cache.lastUsed = c - cacheBegin;
LastUsedCacheElement() = c - cacheBegin;
}
return c;

View File

@@ -17,9 +17,19 @@
// check for compiler support of thread-specific variables
// ----------------------------------------------------------------------------
#ifdef HAVE___THREAD_KEYWORD
// when not using threads at all, there is no need for thread-specific
// values to be really thread-specific
#if !wxUSE_THREADS
#define wxHAS_COMPILER_TLS
#define wxTHREAD_SPECIFIC_DECL
// __thread keyword is supported if configure detected it or when using mingw32
// >= 4.3 which is known to have it too
#elif defined(HAVE___THREAD_KEYWORD) || \
(defined(__MINGW32__) && wxCHECK_GCC_VERSION(4, 3))
#define wxHAS_COMPILER_TLS
#define wxTHREAD_SPECIFIC_DECL __thread
// MSVC has its own version which might be supported by some other Windows
// compilers, to be tested
#elif wxCHECK_VISUALC_VERSION(7)
#define wxHAS_COMPILER_TLS
#define wxTHREAD_SPECIFIC_DECL __declspec(thread)
@@ -31,6 +41,8 @@
#ifdef wxHAS_COMPILER_TLS
#define wxTLS_TYPE(T) wxTHREAD_SPECIFIC_DECL T
#define wxTLS_PTR(var) (&(var))
#define wxTLS_VALUE(var) (var)
#else // !wxHAS_COMPILER_TLS
#ifdef __WXMSW__
#include "wx/msw/tls.h"
@@ -41,23 +53,76 @@
#error Neither compiler nor OS support thread-specific variables.
#endif
// wxTlsValue<T> represents a thread-specific value of type T
#include <stdlib.h> // for calloc()
// wxTlsValue<T> represents a thread-specific value of type T but, unlike
// with native compiler thread-specific variables, it behaves like a
// (never NULL) pointer to T and so needs to be dereferenced before use
template <typename T>
class wxTlsValue
{
public:
typedef T ValueType;
wxTlsValue() { *this = static_cast<T>(0); }
wxTlsValue& operator=(T value)
// ctor doesn't do anything, the object is created on first access
//
// FIXME: the thread-specific values are currently not freed under
// Windows, resulting in memory leaks, this must be implemented
// there somehow (probably by keeping a list of all TLS objects
// and cleaning them up in wxThread cleanup)
wxTlsValue()
#ifdef __UNIX__
: m_key(free)
#endif
{
m_key.Set(wxUIntToPtr(value));
return *this;
}
operator T() const { return wxPtrToUInt(m_key.Get()); }
// dtor is only called in the main thread context and so is not enough
// to free memory allocated by us for the other threads, we use
// destructor function when using Pthreads for this (which is not
// called for the main thread as it doesn't call pthread_exit() but
// just to be safe we also reset the key anyhow) and simply leak the
// memory under Windows (see the FIXME above)
~wxTlsValue()
{
void * const value = m_key.Get();
if ( value)
{
free(value);
m_key.Set(NULL);
}
}
// access the object creating it on demand
ValueType *Get()
{
void *value = m_key.Get();
if ( !value )
{
// ValueType must be POD to be used in wxHAS_COMPILER_TLS case
// anyhow (at least gcc doesn't accept non-POD values being
// declared with __thread) so initialize it as a POD too
value = calloc(1, sizeof(ValueType));
if ( !m_key.Set(value) )
{
free(value);
// this will probably result in a crash in the caller but
// it's arguably better to crash immediately instead of
// slowly dying from out-of-memory errors which would
// happen as the next access to this object would allocate
// another ValueType instance and so on forever
value = NULL;
}
}
return static_cast<ValueType *>(value);
}
// pointer-like accessors
ValueType *operator->() { return Get(); }
ValueType& operator*() { return *Get(); }
private:
wxTlsKey m_key;
@@ -66,6 +131,8 @@
};
#define wxTLS_TYPE(T) wxTlsValue<T>
#define wxTLS_PTR(var) (var)
#define wxTLS_VALUE(var) (*(var))
#endif // wxHAS_COMPILER_TLS/!wxHAS_COMPILER_TLS
#endif // _WX_TLS_H_

View File

@@ -11,9 +11,6 @@
#ifndef _WX_UNIX_TLS_H_
#define _WX_UNIX_TLS_H_
#include "wx/intl.h"
#include "wx/log.h"
#include <pthread.h>
// ----------------------------------------------------------------------------
@@ -23,12 +20,13 @@
class wxTlsKey
{
public:
// ctor allocates a new key
wxTlsKey()
// ctor allocates a new key and possibly registering a destructor function
// for it (notice that using destructor function is Pthreads-specific and
// not supported in Win32 implementation)
wxTlsKey(void (*destructor)(void *) = NULL)
{
int rc = pthread_key_create(&m_key, NULL);
if ( rc )
wxLogSysError(_("Creating TLS key failed"), rc);
if ( pthread_key_create(&m_key, destructor) != 0 )
m_key = 0;
}
// return true if the key was successfully allocated
@@ -43,14 +41,7 @@ public:
// change the key value, return true if ok
bool Set(void *value)
{
int rc = pthread_setspecific(m_key, value);
if ( rc )
{
wxLogSysError(_("Failed to set TLS value"));
return false;
}
return true;
return pthread_setspecific(m_key, value) == 0;
}
// free the key

View File

@@ -10,18 +10,59 @@
Macro to be used for thread-specific variables declarations.
This macro can be used to define thread-specific variables of the specified
@a type. Such variables must be global or static. Example of use:
@a type. Such variables must be global or static and must be POD, i.e.
not have any constructors or destructor (even implicitly generated by the
compiler due to use of base classes or members which are not POD in a
struct).
Example of use:
@code
struct PerThreadData
{
... data which will be different for every thread ...
};
static wxTLS_TYPE(PerThreadData *) s_threadPtr;
static wxTLS_TYPE(PerThreadData) s_threadDataVar;
#define s_threadData (wxTLS_VALUE(s_threadDataVar))
... use s_threadData as a variable of type PerThreadData ...
@endcode
Currently only types of size less than size of a pointer are supported.
This limitation will be lifted in the future.
Notice that the use of the ugly wxTLS_VALUE() macro is unfortunately
required if you need to support platforms without native compiler support
for thread-specific variables. If you compile your code only on platforms
which do have such support (recent versions of GNU C++ compiler, Microsoft
Visual C++ and Sun C++ compiler are known to have it), you can avoid it and
use the variable directly.
*/
#define wxTLS_TYPE(type)
#define wxTLS_TYPE(type) compiler-dependent-implementation
/**
Macro to access thread-specific variables.
This macro is used to hide the difference in implementation of
thread-specific variables under different platforms: they can be of type T
used in wxTLS_TYPE() if they are directly supported by the compiler or of
type emulating @c T @c *, i.e. a pointer to this type otherwise. This macro
always returns an expression of type @c T itself.
As shown in wxTLS_TYPE() example, you may want to @c #define a symbol
wrapping a thread-specific variable with this macro. And, as also explained
in wxTLS_TYPE() documentation, you may avoid using it entirely if you
target only recent compilers.
@see wxTLS_PTR()
*/
#define wxTLS_VALUE(var)
/**
Macro to return address of a thread-specific variables.
This macro is similar to wxTLS_VALUE() except that it always returns a
pointer to the type of thread-specific variable.
Notice that this is not a constant expression even if the macro is defined
simply as @c &amp;var -- the value returned is still different for every
thread.
*/
#define wxTLS_PTR(var)

View File

@@ -59,7 +59,6 @@
const size_t wxString::npos = (size_t) -1;
#if wxUSE_STRING_POS_CACHE
wxTLS_TYPE(wxString::Cache) wxString::ms_cache;
// gdb seems to be unable to display thread-local variables correctly, at least
// not my 6.4.98 version under amd64, so provide this debugging helper to do it
@@ -73,11 +72,11 @@ struct wxStrCacheDumper
for ( unsigned n = 0; n < wxString::Cache::SIZE; n++ )
{
const wxString::Cache::Element&
c = wxString::ms_cache.cached[n];
c = wxString::GetCacheBegin()[n];
printf("\t%u%s\t%p: pos=(%lu, %lu), len=%ld\n",
n,
n == wxString::ms_cache.lastUsed ? " [*]" : "",
n == wxString::LastUsedCacheElement() ? " [*]" : "",
c.str,
(unsigned long)c.pos,
(unsigned long)c.impl,

View File

@@ -13,7 +13,6 @@ datarootdir = @datarootdir@
INSTALL = @INSTALL@
EXEEXT = @EXEEXT@
WINDRES = @WINDRES@
REZ = @REZ@
SETFILE = @SETFILE@
BK_DEPS = @BK_DEPS@
BK_MAKE_PCH = @BK_MAKE_PCH@
@@ -101,6 +100,7 @@ TEST_OBJECTS = \
test_textfiletest.o \
test_atomic.o \
test_queue.o \
test_tls.o \
test_uris.o \
test_vectors.o \
test_evtconnection.o \
@@ -165,20 +165,12 @@ COND_MONOLITHIC_0___WXLIB_XML_p = \
@COND_USE_GUI_1@__test_gui___depname = test_gui$(EXEEXT)
@COND_PLATFORM_MAC_0@__test_gui___mac_setfilecmd = @true
@COND_PLATFORM_MAC_1@__test_gui___mac_setfilecmd = \
@COND_PLATFORM_MAC_1@ $(SETFILE) -a C test_gui$(EXEEXT)
@COND_PLATFORM_MAC_1@__test_gui___mac_rezcmd = $(__MACOSX_RESOURCES_p_1)
@COND_WXUNIV_1@__WXUNIV_DEFINE_p_4 = -d __WXUNIVERSAL__
@COND_PLATFORM_MAC_1@ $(SETFILE) -t APPL test_gui$(EXEEXT)
@COND_WXUNIV_1@__WXUNIV_DEFINE_p_5 = --define __WXUNIVERSAL__
@COND_USE_EXCEPTIONS_0@__EXCEPTIONS_DEFINE_p_4 = -d wxNO_EXCEPTIONS
@COND_USE_EXCEPTIONS_0@__EXCEPTIONS_DEFINE_p_5 = --define wxNO_EXCEPTIONS
@COND_USE_RTTI_0@__RTTI_DEFINE_p_4 = -d wxNO_RTTI
@COND_USE_RTTI_0@__RTTI_DEFINE_p_5 = --define wxNO_RTTI
@COND_USE_THREADS_0@__THREAD_DEFINE_p_4 = -d wxNO_THREADS
@COND_USE_THREADS_0@__THREAD_DEFINE_p_5 = --define wxNO_THREADS
@COND_SHARED_1@__DLLFLAG_p_4 = -d WXUSINGDLL
@COND_SHARED_1@__DLLFLAG_p_5 = --define WXUSINGDLL
@COND_TOOLKIT_MSW@__RCDEFDIR_p = -i \
@COND_TOOLKIT_MSW@ $(LIBDIRNAME)/wx/include/$(TOOLCHAIN_FULLNAME)
@COND_TOOLKIT_MSW@__RCDEFDIR_p_1 = --include-dir \
@COND_TOOLKIT_MSW@ $(LIBDIRNAME)/wx/include/$(TOOLCHAIN_FULLNAME)
@COND_PLATFORM_WIN32_1@__test_gui___win32rc = test_gui_sample_rc.o
@@ -217,12 +209,6 @@ COND_MONOLITHIC_0___WXLIB_CORE_p = \
@COND_ICC_PCH_1@ .pch/testprec_printfbench/testprec.h.gch
@COND_USE_PCH_1@___pch_testprec_printfbench_testprec_h_gch___depname \
@COND_USE_PCH_1@ = .pch/testprec_printfbench/testprec.h.gch
COND_TOOLKIT_MAC___MACOSX_RESOURCES_p_1 = $(REZ) -d __DARWIN__ -t APPL -d \
__WX$(TOOLKIT)__ $(__WXUNIV_DEFINE_p_4) $(__EXCEPTIONS_DEFINE_p_4) \
$(__RTTI_DEFINE_p_4) $(__THREAD_DEFINE_p_4) -i $(srcdir) $(__DLLFLAG_p_4) -i \
$(srcdir)/../samples $(__RCDEFDIR_p) -i $(top_srcdir)/include -o \
test_gui$(EXEEXT) Carbon.r sample.r
@COND_TOOLKIT_MAC@__MACOSX_RESOURCES_p_1 = $(COND_TOOLKIT_MAC___MACOSX_RESOURCES_p_1)
@COND_WXUNIV_1@__WXUNIV_DEFINE_p = -D__WXUNIVERSAL__
@COND_USE_EXCEPTIONS_0@__EXCEPTIONS_DEFINE_p = -DwxNO_EXCEPTIONS
@COND_USE_RTTI_0@__RTTI_DEFINE_p = -DwxNO_RTTI
@@ -274,7 +260,7 @@ test$(EXEEXT): $(TEST_OBJECTS)
@COND_USE_GUI_1@test_gui$(EXEEXT): $(TEST_GUI_OBJECTS) $(__test_gui___win32rc)
@COND_USE_GUI_1@ $(CXX) -o $@ $(TEST_GUI_OBJECTS) $(LDFLAGS) -L$(LIBDIRNAME) $(SAMPLES_RPATH_FLAG) $(CPPUNIT_LIBS) $(LIBS) $(__WXLIB_CORE_p) $(__WXLIB_BASE_p) $(__WXLIB_MONO_p) $(__LIB_TIFF_p) $(__LIB_JPEG_p) $(__LIB_PNG_p) $(EXTRALIBS_FOR_GUI) $(__LIB_ZLIB_p) $(__LIB_REGEX_p) $(__LIB_EXPAT_p) $(EXTRALIBS_FOR_BASE)
@COND_USE_GUI_1@ $(__test_gui___mac_rezcmd)
@COND_USE_GUI_1@
@COND_USE_GUI_1@ $(__test_gui___mac_setfilecmd)
@COND_USE_GUI_1@ $(SAMPLES_RPATH_POSTLINK)
@@ -486,6 +472,9 @@ test_atomic.o: $(srcdir)/thread/atomic.cpp $(TEST_ODEP)
test_queue.o: $(srcdir)/thread/queue.cpp $(TEST_ODEP)
$(CXXC) -c -o $@ $(TEST_CXXFLAGS) $(srcdir)/thread/queue.cpp
test_tls.o: $(srcdir)/thread/tls.cpp $(TEST_ODEP)
$(CXXC) -c -o $@ $(TEST_CXXFLAGS) $(srcdir)/thread/tls.cpp
test_uris.o: $(srcdir)/uris/uris.cpp $(TEST_ODEP)
$(CXXC) -c -o $@ $(TEST_CXXFLAGS) $(srcdir)/uris/uris.cpp

View File

@@ -176,7 +176,8 @@ BENCHMARK_FUNC(BoostTLS)
BENCHMARK_FUNC(wxTLS)
{
static wxTLS_TYPE(int) s_global;
static wxTLS_TYPE(int) s_globalVar;
#define s_global wxTLS_VALUE(s_globalVar)
for ( int n = 0; n < NUM_ITER; n++ )
{

View File

@@ -87,6 +87,7 @@ TEST_OBJECTS = \
$(OBJS)\test_textfiletest.obj \
$(OBJS)\test_atomic.obj \
$(OBJS)\test_queue.obj \
$(OBJS)\test_tls.obj \
$(OBJS)\test_uris.obj \
$(OBJS)\test_vectors.obj \
$(OBJS)\test_evtconnection.obj \
@@ -515,6 +516,9 @@ $(OBJS)\test_atomic.obj: .\thread\atomic.cpp
$(OBJS)\test_queue.obj: .\thread\queue.cpp
$(CXX) -q -c -P -o$@ $(TEST_CXXFLAGS) .\thread\queue.cpp
$(OBJS)\test_tls.obj: .\thread\tls.cpp
$(CXX) -q -c -P -o$@ $(TEST_CXXFLAGS) .\thread\tls.cpp
$(OBJS)\test_uris.obj: .\uris\uris.cpp
$(CXX) -q -c -P -o$@ $(TEST_CXXFLAGS) .\uris\uris.cpp

View File

@@ -79,6 +79,7 @@ TEST_OBJECTS = \
$(OBJS)\test_textfiletest.o \
$(OBJS)\test_atomic.o \
$(OBJS)\test_queue.o \
$(OBJS)\test_tls.o \
$(OBJS)\test_uris.o \
$(OBJS)\test_vectors.o \
$(OBJS)\test_evtconnection.o \
@@ -493,6 +494,9 @@ $(OBJS)\test_atomic.o: ./thread/atomic.cpp
$(OBJS)\test_queue.o: ./thread/queue.cpp
$(CXX) -c -o $@ $(TEST_CXXFLAGS) $(CPPDEPS) $<
$(OBJS)\test_tls.o: ./thread/tls.cpp
$(CXX) -c -o $@ $(TEST_CXXFLAGS) $(CPPDEPS) $<
$(OBJS)\test_uris.o: ./uris/uris.cpp
$(CXX) -c -o $@ $(TEST_CXXFLAGS) $(CPPDEPS) $<

View File

@@ -80,6 +80,7 @@ TEST_OBJECTS = \
$(OBJS)\test_textfiletest.obj \
$(OBJS)\test_atomic.obj \
$(OBJS)\test_queue.obj \
$(OBJS)\test_tls.obj \
$(OBJS)\test_uris.obj \
$(OBJS)\test_vectors.obj \
$(OBJS)\test_evtconnection.obj \
@@ -600,6 +601,9 @@ $(OBJS)\test_atomic.obj: .\thread\atomic.cpp
$(OBJS)\test_queue.obj: .\thread\queue.cpp
$(CXX) /c /nologo /TP /Fo$@ $(TEST_CXXFLAGS) .\thread\queue.cpp
$(OBJS)\test_tls.obj: .\thread\tls.cpp
$(CXX) /c /nologo /TP /Fo$@ $(TEST_CXXFLAGS) .\thread\tls.cpp
$(OBJS)\test_uris.obj: .\uris\uris.cpp
$(CXX) /c /nologo /TP /Fo$@ $(TEST_CXXFLAGS) .\uris\uris.cpp

View File

@@ -292,6 +292,7 @@ TEST_OBJECTS = &
$(OBJS)\test_textfiletest.obj &
$(OBJS)\test_atomic.obj &
$(OBJS)\test_queue.obj &
$(OBJS)\test_tls.obj &
$(OBJS)\test_uris.obj &
$(OBJS)\test_vectors.obj &
$(OBJS)\test_evtconnection.obj &
@@ -546,6 +547,9 @@ $(OBJS)\test_atomic.obj : .AUTODEPEND .\thread\atomic.cpp
$(OBJS)\test_queue.obj : .AUTODEPEND .\thread\queue.cpp
$(CXX) -bt=nt -zq -fo=$^@ $(TEST_CXXFLAGS) $<
$(OBJS)\test_tls.obj : .AUTODEPEND .\thread\tls.cpp
$(CXX) -bt=nt -zq -fo=$^@ $(TEST_CXXFLAGS) $<
$(OBJS)\test_uris.obj : .AUTODEPEND .\uris\uris.cpp
$(CXX) -bt=nt -zq -fo=$^@ $(TEST_CXXFLAGS) $<

View File

@@ -73,6 +73,7 @@
textfile/textfiletest.cpp
thread/atomic.cpp
thread/queue.cpp
thread/tls.cpp
uris/uris.cpp
vectors/vectors.cpp
weakref/evtconnection.cpp

View File

@@ -413,6 +413,10 @@ SOURCE=.\streams\textstreamtest.cpp
# End Source File
# Begin Source File
SOURCE=.\thread\tls.cpp
# End Source File
# Begin Source File
SOURCE=.\strings\tokenizer.cpp
# End Source File
# Begin Source File

View File

@@ -786,6 +786,8 @@
RelativePath=".\textfile\textfiletest.cpp"/>
<File
RelativePath=".\streams\textstreamtest.cpp"/>
<File
RelativePath=".\thread\tls.cpp"/>
<File
RelativePath=".\strings\tokenizer.cpp"/>
<File

View File

@@ -1004,6 +1004,9 @@
<File
RelativePath=".\streams\textstreamtest.cpp"
/>
<File
RelativePath=".\thread\tls.cpp"
/>
<File
RelativePath=".\strings\tokenizer.cpp"
/>

128
tests/thread/tls.cpp Normal file
View File

@@ -0,0 +1,128 @@
///////////////////////////////////////////////////////////////////////////////
// Name: tests/thread/tls.cpp
// Purpose: wxTlsValue unit test
// Author: Vadim Zeitlin
// Created: 2008-08-28
// RCS-ID: $Id$
// Copyright: (c) 2008 Vadim Zeitlin
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#endif // WX_PRECOMP
#include "wx/thread.h"
#include "wx/tls.h"
// ----------------------------------------------------------------------------
// globals
// ----------------------------------------------------------------------------
namespace
{
// NB: this struct must be a POD, so don't use wxString as its member
struct PerThreadData
{
const char *name;
int number;
};
wxTLS_TYPE(PerThreadData) gs_threadDataVar;
#define gs_threadData wxTLS_VALUE(gs_threadDataVar)
wxTLS_TYPE(int) gs_threadIntVar;
#define gs_threadInt wxTLS_VALUE(gs_threadIntVar)
// ----------------------------------------------------------------------------
// test thread
// ----------------------------------------------------------------------------
// this thread arbitrarily modifies all global thread-specific variables to
// make sure that the changes in it are not visible from the main thread
class TLSTestThread : public wxThread
{
public:
// ctor both creates and starts the thread
TLSTestThread() : wxThread(wxTHREAD_JOINABLE) { Create(); Run(); }
virtual void *Entry()
{
gs_threadInt = 17;
gs_threadData.name = "worker";
gs_threadData.number = 2;
CPPUNIT_ASSERT_EQUAL( "worker", gs_threadData.name );
CPPUNIT_ASSERT_EQUAL( 2, gs_threadData.number );
return NULL;
}
};
} // anonymous namespace
// ----------------------------------------------------------------------------
// test class
// ----------------------------------------------------------------------------
class TLSTestCase : public CppUnit::TestCase
{
public:
TLSTestCase() { }
private:
CPPUNIT_TEST_SUITE( TLSTestCase );
CPPUNIT_TEST( TestInt );
CPPUNIT_TEST( TestStruct );
CPPUNIT_TEST_SUITE_END();
void TestInt();
void TestStruct();
DECLARE_NO_COPY_CLASS(TLSTestCase)
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( TLSTestCase );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TLSTestCase, "TLSTestCase" );
void TLSTestCase::TestInt()
{
CPPUNIT_ASSERT_EQUAL( 0, gs_threadInt );
gs_threadInt++;
CPPUNIT_ASSERT_EQUAL( 1, gs_threadInt );
TLSTestThread().Wait();
CPPUNIT_ASSERT_EQUAL( 1, gs_threadInt );
}
void TLSTestCase::TestStruct()
{
CPPUNIT_ASSERT_EQUAL( "", gs_threadData.name );
CPPUNIT_ASSERT_EQUAL( 0, gs_threadData.number );
gs_threadData.name = "main";
gs_threadData.number = 1;
CPPUNIT_ASSERT_EQUAL( 1, gs_threadData.number );
TLSTestThread().Wait();
CPPUNIT_ASSERT_EQUAL( "main", gs_threadData.name );
CPPUNIT_ASSERT_EQUAL( 1, gs_threadData.number );
}