修改BOOST(二)
boost提供的thread_specific_ptr用起来很简便。但是TLS的可用索引数量是有限的。特别是在的DLL中,如果第一个进程加载的DLL模块用去了n个索引,第二个进程加载相同模块时可用索引就减少n个,Windows下可用的索引也就一千个左右(我没有去查资料,只是凭记忆写的,如果有错误,可以在下面的讨论中指出,不过大概是这个意思)。不过可以只用一个TLS索引来保存所有的与线程相关的数据。下面是源代码。
1.头文件vtss.hpp
//vtss.hpp。虚拟TSS索引
#ifndef _VTSS_H
#define _VTSS_H
#include <boost/thread/tss.hpp>
namespace boost {
namespace detail {
class vtss
{
public:
vtss(void (*cleanup)(void*)=0);
~vtss();
void* get() const;
bool set(void* value);
private:
unsigned int key;
};
}
template <typename T>
class vthread_specific_ptr : private noncopyable
{
public:
vthread_specific_ptr() : m_tss(&thread_specific_ptr<T>::cleanup) { }
T* get() const { return static_cast<T*>(m_tss.get()); }
T* operator->() const { return get(); }
T& operator*() const { return *get(); }
T* release() { T* temp = get(); m_tss.set(0); return temp; }
void reset(T* p=0) { T* cur = get(); if (cur == p) return; delete cur; m_tss.set(p); }
private:
static void cleanup(void* p) { delete static_cast<T*>(p); }
mutable detail::vtss m_tss;
};
} // namespace boost
#endif
2.vtss.cpp文件
#include <boost/thread/tss.hpp>
#include <boost/thread/vtss.hpp>
#include <boost/thread/mutex.hpp>
#include <map>
#include <set>
namespace {
class cleanup_info
{
public:
cleanup_info(void (*_cleanup)(void *)=0,void *_data=0)
:cleanup(_cleanup),data(_data){}
~cleanup_info(){if(cleanup && data)cleanup(data);}
void reset(){data=0;}
void (*cleanup)(void *);
void *data;
};
typedef std::map<int, cleanup_info> cleanup_handlers;
boost::thread_specific_ptr<cleanup_handlers> ptr_global;
class tsskey
{
tsskey();
public:
static tsskey &instance();
unsigned int alloc();
void free(int key);
boost::mutex m_lock;
std::set<unsigned int> allkey;
unsigned int key;
};
tsskey::tsskey():key(0)
{
}
tsskey &tsskey::instance()
{
static tsskey tss;
return tss;
}
unsigned int tsskey::alloc()
{
boost::mutex::scoped_lock lock(m_lock);
while(allkey.find(++key)!=allkey.end());
return key;
}
void tsskey::free(int key)
{
boost::mutex::scoped_lock lock(m_lock);
allkey.erase(key);
}
}
namespace boost {
namespace detail {
vtss::vtss(void (*cleanup)(void*))
:m_cleanup(cleanup)
{
m_key=tsskey::instance().alloc();
}
vtss::~vtss()
{
tsskey::instance().free(m_key);
}
void* vtss::get() const
{
cleanup_handlers *p=ptr_global.get();
if(p)
{
cleanup_handlers::iterator it=p->find(m_key);
if(it!=p->end())
{
return it->second.data;
}
}
return 0;
}
void vtss::set(void* value)
{
cleanup_handlers *p=ptr_global.get();
if(!p)
{
p=new cleanup_handlers;
ptr_global.reset(p);
}
if(value)
{
cleanup_info info(m_cleanup, value);
(*p)[m_key]=info;
info.reset();
}
else
{
p->erase(m_key);
}
}
} // namespace detail
} // namespace boost
3.Vtss.hpp文件放到<boost>/boost/thread/目录下,vtss.cpp文件放到<boost>/libs/thread/src。它们的用法和原先的pthread_specific_ptr的唯一区别是多了个“v”,也就是vpthread_specific_ptr。另外,还可以将vpthread_specific_ptr的private noncopyable去掉,这时如果你的vpthread_specific_ptr在类对象里面,则该类对象也可以互相复制。
其次的实现是读写锁,下回吧。