From 8b73c5318ccc837aff38c0e5a824bc4fd56c75c8 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 29 Aug 2008 23:28:42 +0000 Subject: [PATCH] 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 --- include/wx/msw/tls.h | 20 +----- include/wx/string.h | 39 +++++++---- include/wx/tls.h | 81 +++++++++++++++++++++-- include/wx/unix/tls.h | 23 ++----- interface/wx/tls.h | 51 +++++++++++++-- src/common/string.cpp | 5 +- tests/Makefile.in | 23 ++----- tests/benchmarks/tls.cpp | 3 +- tests/makefile.bcc | 4 ++ tests/makefile.gcc | 4 ++ tests/makefile.vc | 4 ++ tests/makefile.wat | 4 ++ tests/test.bkl | 1 + tests/test_test.dsp | 4 ++ tests/test_vc7_test.vcproj | 2 + tests/test_vc8_test.vcproj | 3 + tests/thread/tls.cpp | 128 +++++++++++++++++++++++++++++++++++++ 17 files changed, 318 insertions(+), 81 deletions(-) create mode 100644 tests/thread/tls.cpp diff --git a/include/wx/msw/tls.h b/include/wx/msw/tls.h index f946e12bdc..eee0096c97 100644 --- a/include/wx/msw/tls.h +++ b/include/wx/msw/tls.h @@ -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: diff --git a/include/wx/string.h b/include/wx/string.h index 845316e883..08eafdc4b4 100644 --- a/include/wx/string.h +++ b/include/wx/string.h @@ -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; diff --git a/include/wx/tls.h b/include/wx/tls.h index 9d1d6373b3..1902716068 100644 --- a/include/wx/tls.h +++ b/include/wx/tls.h @@ -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 represents a thread-specific value of type T + #include // for calloc() + + // wxTlsValue 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 class wxTlsValue { public: typedef T ValueType; - wxTlsValue() { *this = static_cast(0); } + // 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 + { + } - wxTlsValue& operator=(T value) + // 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() { - m_key.Set(wxUIntToPtr(value)); + void * const value = m_key.Get(); + if ( value) + { + free(value); + m_key.Set(NULL); + } + } - return *this; + // 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(value); } - operator T() const { return wxPtrToUInt(m_key.Get()); } + // pointer-like accessors + ValueType *operator->() { return Get(); } + ValueType& operator*() { return *Get(); } private: wxTlsKey m_key; @@ -66,6 +131,8 @@ }; #define wxTLS_TYPE(T) wxTlsValue + #define wxTLS_PTR(var) (var) + #define wxTLS_VALUE(var) (*(var)) #endif // wxHAS_COMPILER_TLS/!wxHAS_COMPILER_TLS #endif // _WX_TLS_H_ diff --git a/include/wx/unix/tls.h b/include/wx/unix/tls.h index 776494d015..5f5744c164 100644 --- a/include/wx/unix/tls.h +++ b/include/wx/unix/tls.h @@ -11,9 +11,6 @@ #ifndef _WX_UNIX_TLS_H_ #define _WX_UNIX_TLS_H_ -#include "wx/intl.h" -#include "wx/log.h" - #include // ---------------------------------------------------------------------------- @@ -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 diff --git a/interface/wx/tls.h b/interface/wx/tls.h index 6dcb387227..aa86dec704 100644 --- a/interface/wx/tls.h +++ b/interface/wx/tls.h @@ -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) 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_TYPE(type) +#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 &var -- the value returned is still different for every + thread. + */ +#define wxTLS_PTR(var) diff --git a/src/common/string.cpp b/src/common/string.cpp index c97125d2c4..85b3090533 100644 --- a/src/common/string.cpp +++ b/src/common/string.cpp @@ -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, diff --git a/tests/Makefile.in b/tests/Makefile.in index e10f222a21..363babc150 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -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 diff --git a/tests/benchmarks/tls.cpp b/tests/benchmarks/tls.cpp index de6a4c2737..b187566c17 100644 --- a/tests/benchmarks/tls.cpp +++ b/tests/benchmarks/tls.cpp @@ -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++ ) { diff --git a/tests/makefile.bcc b/tests/makefile.bcc index 6915d58ff4..5161340ad7 100644 --- a/tests/makefile.bcc +++ b/tests/makefile.bcc @@ -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 diff --git a/tests/makefile.gcc b/tests/makefile.gcc index 2d220fceff..8c44c497e0 100644 --- a/tests/makefile.gcc +++ b/tests/makefile.gcc @@ -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) $< diff --git a/tests/makefile.vc b/tests/makefile.vc index 280abafcf0..531a13c6eb 100644 --- a/tests/makefile.vc +++ b/tests/makefile.vc @@ -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 diff --git a/tests/makefile.wat b/tests/makefile.wat index ece7379d06..d786cde266 100644 --- a/tests/makefile.wat +++ b/tests/makefile.wat @@ -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) $< diff --git a/tests/test.bkl b/tests/test.bkl index 0d15a28298..d66a473b50 100644 --- a/tests/test.bkl +++ b/tests/test.bkl @@ -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 diff --git a/tests/test_test.dsp b/tests/test_test.dsp index febddb5fa5..def8ffb07f 100644 --- a/tests/test_test.dsp +++ b/tests/test_test.dsp @@ -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 diff --git a/tests/test_vc7_test.vcproj b/tests/test_vc7_test.vcproj index ca8b6b7268..349b30e908 100644 --- a/tests/test_vc7_test.vcproj +++ b/tests/test_vc7_test.vcproj @@ -786,6 +786,8 @@ RelativePath=".\textfile\textfiletest.cpp"/> + + diff --git a/tests/thread/tls.cpp b/tests/thread/tls.cpp new file mode 100644 index 0000000000..4504e143b7 --- /dev/null +++ b/tests/thread/tls.cpp @@ -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 ); +} + -- 2.45.2