/* PCSX2 - PS2 Emulator for PCs * Copyright (C) 2002-2010 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with PCSX2. * If not, see . */ #pragma once #include "Threading.h" #if PCSX2_THREAD_LOCAL # define DeclareTls(x) __threadlocal x #else # define DeclareTls(x) Threading::TlsVariable #endif namespace Threading { // -------------------------------------------------------------------------------------- // TlsVariable - Thread local storage // -------------------------------------------------------------------------------------- // Wrapper class for pthread_getspecific, which is pthreads language for "thread local // storage." This class enables code to act as a drop-in replacement for compiler-native // thread local storage (typically specified via __threadlocal). Mac OS/X (Darwin) does // not have TLS, which is the main reason for this class existing. // // Performance considerations: While certainly convenient, performance of this class can // be sub-optimal when the operator overloads are used, since each one will most likely // result in repeated calls to pthread_getspecific. (if the function inlines then it // should actually optimize well enough, but I doubt it does). // template< typename T > class BaseTlsVariable { DeclareNoncopyableObject(BaseTlsVariable); protected: pthread_key_t m_thread_key; bool m_IsDisposed; public: BaseTlsVariable(); virtual ~BaseTlsVariable() throw() { Dispose(); } T* GetPtr() const; T& GetRef() const { return *GetPtr(); } operator T&() const { return GetRef(); } T* operator->() const { return GetPtr(); } void Dispose() { if (!m_IsDisposed) { m_IsDisposed = true; KillKey(); } } protected: void CreateKey(); void KillKey(); virtual void CreateInstance( T* result ) const { new (result) T(); } static void _aligned_delete_and_free( void* ptr ) { if (!ptr) return; ((T*)ptr)->~T(); _aligned_free(ptr); } }; template< typename T > class TlsVariable : public BaseTlsVariable { DeclareNoncopyableObject(TlsVariable); protected: T m_initval; public: TlsVariable() {} TlsVariable( const T& initval ) : m_initval(initval) { } // This is needed; The C++ standard likes making life suck for programmers. using BaseTlsVariable::GetRef; virtual ~TlsVariable() throw() { // disable the parent cleanup. This leaks memory blocks, but its necessary because // TLS is expected to be persistent until the very end of execution on the main thread. // Killing the pthread_key at all will lead to the console logger death, etc. // DON'T REMOVE this->, the "official"[ly stupid] C++ standard requires it because of its // insistence that variables be looked up during initial template parsing and not during // instantiation. this->m_IsDisposed = true; } TlsVariable& operator=( const T& src ) { GetRef() = src; return *this; } bool operator==( const T& src ) const { return GetRef() == src; } bool operator!=( const T& src ) const { return GetRef() != src; } bool operator>( const T& src ) const { return GetRef() > src; } bool operator<( const T& src ) const { return GetRef() < src; } bool operator>=( const T& src ) const { return GetRef() >= src; } bool operator<=( const T& src ) const { return GetRef() <= src; } T operator+( const T& src ) const { return GetRef() + src; } T operator-( const T& src ) const { return GetRef() - src; } void operator+=( const T& src ) { GetRef() += src; } void operator-=( const T& src ) { GetRef() -= src; } protected: virtual void CreateInstance( T* result ) const { new (result) T(m_initval); } }; }; template< typename T > Threading::BaseTlsVariable::BaseTlsVariable() { m_IsDisposed = false; CreateKey(); } template< typename T > void Threading::BaseTlsVariable::KillKey() { if (!m_thread_key) return; // Delete the handle for the current thread (which should always be the main/UI thread!) // This is needed because pthreads does *not* clean up the dangling objects when you delete // the key. The TLS for the process main thread will only be deleted when the process // ends; which is too damn late (it shows up int he leaked memory blocks). BaseTlsVariable::_aligned_delete_and_free( pthread_getspecific(m_thread_key) ); pthread_key_delete( m_thread_key ); m_thread_key = 0; } template< typename T > T* Threading::BaseTlsVariable::GetPtr() const { T* result = (T*)pthread_getspecific( m_thread_key ); if( result == NULL ) { pthread_setspecific( m_thread_key, result = (T*)_aligned_malloc(sizeof(T), 16) ); CreateInstance(result); if( result == NULL ) throw Exception::OutOfMemory( L"thread local storage variable instance" ); } return result; } template< typename T > void Threading::BaseTlsVariable::CreateKey() { if( 0 != pthread_key_create(&m_thread_key, BaseTlsVariable::_aligned_delete_and_free) ) { pxFailRel( "Thread Local Storage Error: key creation failed. This will most likely lead to a rapid application crash." ); } }