};
-CFileCache::CFileCache()
+CFileCache::CFileCache() : m_seekEvent(false,true)
{
m_bDeleteCache = true;
m_nSeekResult = 0;
CLog::Log(LOGINFO, "CFileCache::Process - Hit eof.");
m_pCache->EndOfInput();
- // since there is no more to read - wait either for seek or close
- // WaitForSingleObject is CThread::WaitForSingleObject that will also listen to the
- // end thread event.
- int nRet = CThread::WaitForSingleObject(m_seekEvent.GetHandle(), INFINITE);
- if (nRet == WAIT_OBJECT_0)
+ // The thread event will now also cause the wait of an event to return a false.
+ if (m_seekEvent.Wait())
{
m_pCache->ClearEndOfInput();
m_seekEvent.Set(); // hack so that later we realize seek is needed
*/
#include "Event.h"
-#include "utils/log.h"
+#include "utils/TimeUtils.h"
+#include "PlatformDefs.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
-CEvent::CEvent(bool manual)
-{
- if(manual)
- m_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- else
- m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+void CEvent::Interrupt()
+{
+ CSingleLock lock(mutex);
+ interrupted = true;
+ condVar.notifyAll();
}
-CEvent::CEvent(const CEvent& src)
+bool CEvent::Wait()
{
- if(DuplicateHandle( GetCurrentProcess()
- , src.m_hEvent
- , GetCurrentProcess()
- , &m_hEvent
- , 0
- , TRUE
- , DUPLICATE_SAME_ACCESS ))
- {
- CLog::Log(LOGERROR, "CEvent - failed to duplicate handle");
- m_hEvent = INVALID_HANDLE_VALUE;
- }
-}
+ CSingleLock lock(mutex);
+ interrupted = false;
+ Guard g(interruptible ? this : NULL);
-CEvent::~CEvent()
-{
- CloseHandle(m_hEvent);
-}
+ while (!setState && !interrupted)
+ condVar.wait(mutex);
-CEvent& CEvent::operator=(const CEvent& src)
-{
- CloseHandle(m_hEvent);
-
- if(DuplicateHandle( GetCurrentProcess()
- , src.m_hEvent
- , GetCurrentProcess()
- , &m_hEvent
- , 0
- , TRUE
- , DUPLICATE_SAME_ACCESS ))
- {
- CLog::Log(LOGERROR, "CEvent - failed to duplicate handle");
- m_hEvent = INVALID_HANDLE_VALUE;
- }
- return *this;
-}
+ bool ret = setState;
+ if (!manualReset)
+ setState = false;
-
-void CEvent::Wait()
-{
- if (m_hEvent)
- {
- WaitForSingleObject(m_hEvent, INFINITE);
- }
+ return ret;
}
-void CEvent::Set()
+bool CEvent::WaitMSec(unsigned int milliSeconds)
{
- if (m_hEvent) SetEvent(m_hEvent);
-}
+ CSingleLock lock(mutex);
+ interrupted = false;
+ Guard g(interruptible ? this : NULL);
-void CEvent::Reset()
-{
+ unsigned int startTime = CTimeUtils::GetTimeMS();
+ unsigned int remainingTime = milliSeconds;
+ while(!setState && !interrupted)
+ {
+ XbmcThreads::ConditionVariable::TimedWaitResponse resp = condVar.wait(mutex,remainingTime);
- if (m_hEvent) ResetEvent(m_hEvent);
-}
+ if (setState)
+ return true;
-HANDLE CEvent::GetHandle()
-{
- return m_hEvent;
-}
+ if (resp == XbmcThreads::ConditionVariable::TIMEDOUT)
+ return false;
-bool CEvent::WaitMSec(unsigned int milliSeconds)
-{
+ unsigned int elapsedTimeMillis = CTimeUtils::GetTimeMS() - startTime;
+ if (elapsedTimeMillis > milliSeconds)
+ return false;
- if (m_hEvent)
- {
- DWORD dwResult = WaitForSingleObject(m_hEvent, milliSeconds);
- if (dwResult == WAIT_OBJECT_0) return true;
+ remainingTime = milliSeconds - elapsedTimeMillis;
}
- return false;
+
+ bool ret = setState;
+ if (!manualReset)
+ setState = false;
+
+ return ret;
}
-// Event.h: interface for the CEvent class.
-//
-//////////////////////////////////////////////////////////////////////
-
-#if !defined(AFX_EVENT_H__724ADE14_0F5C_4836_B995_08FFAA97D6B9__INCLUDED_)
-#define AFX_EVENT_H__724ADE14_0F5C_4836_B995_08FFAA97D6B9__INCLUDED_
+/*
+ * Copyright (C) 2005-2011 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program 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 XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
-#if _MSC_VER > 1000
#pragma once
-#endif // _MSC_VER > 1000
+#include "threads/Condition.h"
+#include "threads/Interruptible.h"
-/*
-* XBMC Media Center
-* Copyright (c) 2002 Frodo
-* Portions Copyright (c) by the authors of ffmpeg and xvid
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program 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 this program; if not, write to the Free Software
-* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#if defined(_WIN32)
-#include <windows.h>
-#else
-#include "PlatformInclude.h"
-#endif
-
-class CEvent
+/**
+ * This is an Event class built from a ConditionVariable. The Event adds the state
+ * that the condition is gating as well as the mutex/lock.
+ *
+ * This Event can be 'interruptible' (even though there is only a single place
+ * in the code that uses this behavior).
+ *
+ * This class manages 'spurious returns' from the condition variable.
+ */
+class CEvent : public XbmcThreads::IInterruptible
{
+ bool manualReset;
+ bool setState;
+ bool interrupted;
+ bool interruptible;
+ XbmcThreads::ConditionVariable condVar;
+ CCriticalSection mutex;
+
+ // block the ability to copy
+ inline CEvent& operator=(const CEvent& src) { return *this; }
+ inline CEvent(const CEvent& other) {}
public:
+
+ inline CEvent(bool manual = false, bool interruptible_ = false) :
+ manualReset(manual), setState(false), interrupted(false), interruptible(interruptible_) {}
+ inline void Reset() { CSingleLock lock(mutex); setState = false; }
+ inline void Set() { CSingleLock lock(mutex); setState = true; condVar.notifyAll(); }
+
+ virtual void Interrupt();
+ inline bool wasInterrupted() { CSingleLock lock(mutex); return interrupted; }
+
+ /**
+ * This will wait up to 'milliSeconds' milliseconds for the Event
+ * to be triggered. The method will return 'true' if the Event
+ * was triggered. If it was either interrupted, or it timed out
+ * it will return false. To determine if it was interrupted you can
+ * use 'wasInterrupted()' call prior to any further call to a
+ * Wait* method.
+ */
bool WaitMSec(unsigned int milliSeconds);
- HANDLE GetHandle();
- void Reset();
- void Set();
- void Wait();
- CEvent(bool manual = false);
- CEvent(const CEvent& event);
- CEvent& operator=(const CEvent& src);
-
- virtual ~CEvent();
-
-protected:
- HANDLE m_hEvent;
+
+ /**
+ * This will wait for the Event to be triggered. The method will return
+ * 'true' if the Event was triggered. If it was either interrupted
+ * it will return false. To determine if it was interrupted you can
+ * use 'wasInterrupted()' call prior to any further call to a Wait* method.
+ */
+ bool Wait();
};
-#endif // !defined(AFX_EVENT_H__724ADE14_0F5C_4836_B995_08FFAA97D6B9__INCLUDED_)
--- /dev/null
+/*
+ * Copyright (C) 2005-2011 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program 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 XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+
+#include <vector>
+
+#include "threads/Interruptible.h"
+#include "threads/ThreadLocal.h"
+#include "threads/SingleLock.h"
+
+namespace XbmcThreads
+{
+ static CCriticalSection staticMutexForLockingTheListOfIInterruptiblesForCallingInterrupt;
+ static ThreadLocal<std::vector<IInterruptible*> > threadSpecificInterruptibles;
+ static std::vector<IInterruptible*> allInterruptibles;
+
+ static void callInterrupt(std::vector<IInterruptible*> * interruptibles)
+ {
+ if (interruptibles != NULL)
+ {
+ // copy the list in case the Interrupt call modifies it.
+ std::vector<IInterruptible*> list(interruptibles->size());
+ std::vector<IInterruptible*>::iterator iter;
+
+ {
+ CSingleLock lock(staticMutexForLockingTheListOfIInterruptiblesForCallingInterrupt);
+ for (iter=interruptibles->begin(); iter != interruptibles->end(); iter++)
+ list.push_back(*(iter));
+ }
+
+ for (iter=list.begin(); iter != list.end(); iter++)
+ (*iter)->Interrupt();
+ }
+ }
+
+ void IInterruptible::InterruptAll()
+ {
+ callInterrupt(&allInterruptibles);
+ }
+
+ void IInterruptible::InterruptThreadSpecific()
+ {
+ callInterrupt(threadSpecificInterruptibles.get());
+ }
+
+ void IInterruptible::enteringWaitState()
+ {
+ std::vector<IInterruptible*> * cur = threadSpecificInterruptibles.get();
+ if (cur == NULL)
+ {
+ cur = new std::vector<IInterruptible*>();
+ threadSpecificInterruptibles.set(cur);
+ }
+ cur->push_back(this);
+
+ CSingleLock lock(staticMutexForLockingTheListOfIInterruptiblesForCallingInterrupt);
+ allInterruptibles.push_back(this);
+ }
+
+ void static removeIt(std::vector<IInterruptible*> * list, IInterruptible* val)
+ {
+ std::vector<IInterruptible*>::iterator iter;
+ for (iter = list->begin(); iter != list->end(); iter++)
+ {
+ if ((*iter) == val)
+ {
+ list->erase(iter);
+ break;
+ }
+ }
+ }
+
+ void IInterruptible::leavingWaitState()
+ {
+ std::vector<IInterruptible*> * cur = threadSpecificInterruptibles.get();
+ if (cur != NULL)
+ removeIt(cur,this);
+ CSingleLock lock(staticMutexForLockingTheListOfIInterruptiblesForCallingInterrupt);
+ removeIt(&allInterruptibles,this);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2005-2011 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program 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 XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+namespace XbmcThreads
+{
+ /**
+ * This interface is meant to be implemented by operations that
+ * block and can be interrupted.
+ *
+ * The implementer of an IInterruptible must not only handle the Interrupt
+ * callback but also register and unregister the Interruptable before entering
+ * and after leaving a wait state. The Guard class is a helper for the
+ * implementers to do this. As an example:
+ *
+ * class MyInterruptible : public IInterruptible
+ * {
+ * public:
+ * virtual void Interrupt();
+ *
+ * void blockingMethodCall() { XbmcThreads::IInterruptible::Guard g(*this); ... do blocking; }
+ * };
+ *
+ * See CEvent as an example
+ */
+ class IInterruptible
+ {
+ public:
+ virtual void Interrupt() = 0;
+
+ /**
+ * Calling InterruptAll will invoke interrup on all IInterruptibles
+ * currently in a wait state
+ */
+ static void InterruptAll();
+
+ /**
+ * Calling InterruptThreadSpecific will invoke interrup on all IInterruptibles
+ * currently in a wait state in this thread only.
+ */
+ static void InterruptThreadSpecific();
+
+ protected:
+
+ void enteringWaitState();
+ void leavingWaitState();
+
+ class Guard
+ {
+ IInterruptible* interruptible;
+ public:
+ inline Guard(IInterruptible* pinterruptible) : interruptible(pinterruptible) { if (pinterruptible) pinterruptible->enteringWaitState(); }
+ inline ~Guard() { if (interruptible) interruptible->leavingWaitState(); }
+ };
+ };
+}
+
SRCS=Atomics.cpp \
Condition.cpp \
Event.cpp \
+ Interruptible.cpp \
LockFree.cpp \
Semaphore.cpp \
Thread.cpp \
+ ThreadLocal.cpp \
LIB=threads.a
#include "utils/log.h"
#include "utils/TimeUtils.h"
+#include "threads/ThreadLocal.h"
+#include "threads/Interruptible.h"
-#if defined(__APPLE__) || defined(__FreeBSD__)
-//
-// Use pthread's built-in support for TLS, it's more portable.
-//
-static pthread_once_t keyOnce = PTHREAD_ONCE_INIT;
-static pthread_key_t tlsLocalThread = 0;
-
-//
-// Called once and only once.
-//
-static void MakeTlsKeys()
-{
- pthread_key_create(&tlsLocalThread, NULL);
-}
-
-#endif
+static XbmcThreads::ThreadLocal<CThread> currentThread;
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
-CThread::CThread(const char* ThreadName)
+CThread::CThread(const char* ThreadName) : m_StopEvent(true)
{
#if defined(__APPLE__) || defined(__FreeBSD__)
// Initialize thread local storage and local thread pointer.
m_iLastTime = 0;
m_iLastUsage = 0;
m_fLastUsage = 0.0f;
- m_StopEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
m_pRunnable=NULL;
m_ThreadName = ThreadName;
}
-CThread::CThread(IRunnable* pRunnable, const char* ThreadName)
+CThread::CThread(IRunnable* pRunnable, const char* ThreadName) : m_StopEvent(true)
{
#if defined(__APPLE__) || defined(__FreeBSD__)
// Initialize thread local storage and local thread pointer.
m_iLastTime = 0;
m_iLastUsage = 0;
m_fLastUsage = 0.0f;
- m_StopEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
m_pRunnable=pRunnable;
}
m_ThreadHandle = NULL;
- if (m_StopEvent)
- CloseHandle(m_StopEvent);
}
-#ifdef _LINUX
-#if defined(__APPLE__) || defined(__FreeBSD__)
-// Use pthread-based TLS.
-#define LOCAL_THREAD ((CThread* )pthread_getspecific(tlsLocalThread))
-#else
-// Use compiler-based TLS.
-__thread CThread* pLocalThread = NULL;
-#define LOCAL_THREAD pLocalThread
-#endif
+#ifndef _WIN32
void CThread::term_handler (int signum)
{
CLog::Log(LOGERROR,"thread 0x%lx (%lu) got signal %d. calling OnException and terminating thread abnormally.", (long unsigned int)pthread_self(), (long unsigned int)pthread_self(), signum);
- if (LOCAL_THREAD)
+
+ CThread* curThread = currentThread.get();
+ if (curThread)
{
- LOCAL_THREAD->m_bStop = TRUE;
- if (LOCAL_THREAD->m_StopEvent)
- SetEvent(LOCAL_THREAD->m_StopEvent);
+ curThread->m_bStop = TRUE;
+ curThread->m_StopEvent.Set();
+ XbmcThreads::IInterruptible::InterruptThreadSpecific();
- LOCAL_THREAD->OnException();
- if( LOCAL_THREAD->IsAutoDelete() )
- delete LOCAL_THREAD;
+ curThread->OnException();
+ if( curThread->IsAutoDelete() )
+ delete curThread;
}
pthread_exit(NULL);
}
+
int CThread::staticThread(void* data)
#else
DWORD WINAPI CThread::staticThread(LPVOID* data)
CLog::Log(LOGDEBUG,"Thread %s start, auto delete: %d", pThread->m_ThreadName.c_str(), pThread->IsAutoDelete());
+ currentThread.set(pThread);
#ifndef _LINUX
/* install win32 exception translator */
win32_exception::install_handler();
#else
-#if !defined(__APPLE__) && !defined(__FreeBSD__)
- pLocalThread = pThread;
-#endif
struct sigaction action;
action.sa_handler = term_handler;
sigemptyset (&action.sa_mask);
#endif
-#if defined(__APPLE__) || defined(__FreeBSD__)
- // Set the TLS.
- pthread_setspecific(tlsLocalThread, (void*)pThread);
-#endif
-
try
{
pThread->OnStartup();
m_fLastUsage = 0.0f;
m_bAutoDelete = bAutoDelete;
m_bStop = false;
- ::ResetEvent(m_StopEvent);
+ m_StopEvent.Reset();
m_ThreadHandle = (HANDLE)_beginthreadex(NULL, stacksize, (PBEGINTHREADEX_THREADFUNC)staticThread, (void*)this, 0, &m_ThreadId);
void CThread::StopThread(bool bWait /*= true*/)
{
m_bStop = true;
- SetEvent(m_StopEvent);
+ m_StopEvent.Set();
+ XbmcThreads::IInterruptible::InterruptThreadSpecific();
if (m_ThreadHandle && bWait)
{
WaitForThreadExit(INFINITE);
#endif
}
-
-DWORD CThread::WaitForSingleObject(HANDLE hHandle, unsigned int milliseconds)
-{
- if(milliseconds > 10 && IsCurrentThread())
- {
- HANDLE handles[2] = {hHandle, m_StopEvent};
- DWORD result = ::WaitForMultipleObjects(2, handles, false, milliseconds);
-
- if(result == WAIT_TIMEOUT || result == WAIT_OBJECT_0)
- return result;
-
- if( milliseconds == INFINITE )
- return WAIT_ABANDONED;
- else
- return WAIT_TIMEOUT;
- }
- else
- return ::WaitForSingleObject(hHandle, milliseconds);
-}
-
-DWORD CThread::WaitForMultipleObjects(DWORD nCount, HANDLE *lpHandles, BOOL bWaitAll, unsigned int milliseconds)
-{
- // for now not implemented
- return ::WaitForMultipleObjects(nCount, lpHandles, bWaitAll, milliseconds);
-}
-
void CThread::Sleep(unsigned int milliseconds)
{
if(milliseconds > 10 && IsCurrentThread())
- ::WaitForSingleObject(m_StopEvent, milliseconds);
+ m_StopEvent.WaitMSec(milliseconds);
else
::Sleep(milliseconds);
}
+
+
virtual ~CThread();
void Create(bool bAutoDelete = false, unsigned stacksize = 0);
bool WaitForThreadExit(unsigned int milliseconds);
- DWORD WaitForSingleObject(HANDLE hHandle, unsigned int milliseconds);
- DWORD WaitForMultipleObjects(DWORD nCount, HANDLE *lpHandles, BOOL bWaitAll, unsigned int milliseconds);
void Sleep(unsigned int milliseconds);
bool SetPriority(const int iPriority);
void SetPrioritySched_RR(void);
virtual void OnException(){} // signal termination handler
virtual void Process();
-#ifdef _LINUX
- static void term_handler (int signum);
-#endif
-
volatile bool m_bStop;
HANDLE m_ThreadHandle;
private:
ThreadIdentifier ThreadId() const;
bool m_bAutoDelete;
- HANDLE m_StopEvent;
+ CEvent m_StopEvent;
unsigned m_ThreadId; // This value is unreliable on platforms using pthreads
// Use m_ThreadHandle->m_hThread instead
IRunnable* m_pRunnable;
CStdString m_ThreadName;
-private:
+#ifdef _LINUX
+ static void term_handler (int signum);
+#endif
+
#ifndef _WIN32
static int staticThread(void* data);
#else
static DWORD WINAPI staticThread(LPVOID* data);
#endif
+
+private:
};
#endif // !defined(AFX_THREAD_H__ACFB7357_B961_4AC1_9FB2_779526219817__INCLUDED_)
--- /dev/null
+/*
+* Copyright (C) 2005-2011 Team XBMC
+* http://www.xbmc.org
+*
+* This Program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This Program 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 XBMC; see the file COPYING. If not, write to
+* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+* http://www.gnu.org/copyleft/gpl.html
+*
+*/
+
+#include "threads/ThreadLocal.h"
+
+namespace XbmcThreads
+{
+ void ThreadLocalNoCleanup(void*) {}
+}
--- /dev/null
+/*
+* Copyright (C) 2005-2011 Team XBMC
+* http://www.xbmc.org
+*
+* This Program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+*
+* This Program 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 XBMC; see the file COPYING. If not, write to
+* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+* http://www.gnu.org/copyleft/gpl.html
+*
+*/
+
+#pragma once
+
+#include <boost/thread/tss.hpp>
+
+namespace XbmcThreads
+{
+ extern void ThreadLocalNoCleanup(void*);
+
+ /**
+ * A thin wrapper around boost::thread_specific_ptr
+ */
+ template <typename T> class ThreadLocal
+ {
+ boost::thread_specific_ptr<T> value;
+
+ typedef void (*cleanupFunction)(T*);
+ public:
+ inline ThreadLocal() : value((cleanupFunction)ThreadLocalNoCleanup) {}
+ inline T* replace(T* val) { void* ret = value.get(); value.reset(val); return (T*)ret; }
+ inline void set(T* val) { value.reset(val); }
+ inline T* get() { return value.get(); }
+ };
+}
+