pcsx2/common/include/Utilities/TlsVariable.inl

191 lines
5.8 KiB
C++

/* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "Threading.h"
#if PCSX2_THREAD_LOCAL
#define DeclareTls(x) __threadlocal x
#else
#define DeclareTls(x) Threading::TlsVariable<x>
#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<T>);
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<T>
{
DeclareNoncopyableObject(TlsVariable<T>);
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<T>::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<T> &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<T>::BaseTlsVariable()
{
m_IsDisposed = false;
CreateKey();
}
template <typename T>
void Threading::BaseTlsVariable<T>::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<T>::_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<T>::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<T>::CreateKey()
{
if (0 != pthread_key_create(&m_thread_key, BaseTlsVariable<T>::_aligned_delete_and_free)) {
pxFailRel("Thread Local Storage Error: key creation failed. This will most likely lead to a rapid application crash.");
}
}